feat: add optional custom argument topic override via Web UI
- Added optional 'topic' field to BipolarTriggerRequest model - Added topic parameter to force_trigger_argument and force_trigger_argument_from_message_id - Updated run_argument to accept optional custom topic (None=random, ''=no topic, str=custom) - Added topic input field to Web UI trigger-argument section - Updated JS to send topic in API request body - Custom topics bypass the random rotation system, allowing manual theme control
This commit is contained in:
@@ -129,7 +129,7 @@ def trigger_argument(data: BipolarTriggerRequest):
|
|||||||
if message_id:
|
if message_id:
|
||||||
async def trigger_from_message():
|
async def trigger_from_message():
|
||||||
success, error = await force_trigger_argument_from_message_id(
|
success, error = await force_trigger_argument_from_message_id(
|
||||||
channel_id, message_id, globals.client, data.context
|
channel_id, message_id, globals.client, data.context, topic=data.topic
|
||||||
)
|
)
|
||||||
if not success:
|
if not success:
|
||||||
logger.error(f"Failed to trigger argument from message: {error}")
|
logger.error(f"Failed to trigger argument from message: {error}")
|
||||||
@@ -148,8 +148,8 @@ def trigger_argument(data: BipolarTriggerRequest):
|
|||||||
if not channel:
|
if not channel:
|
||||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"Channel {channel_id} not found"})
|
return JSONResponse(status_code=404, content={"status": "error", "message": f"Channel {channel_id} not found"})
|
||||||
|
|
||||||
# Trigger the argument
|
# Trigger the argument with optional custom topic
|
||||||
globals.client.loop.create_task(force_trigger_argument(channel, globals.client, data.context))
|
globals.client.loop.create_task(force_trigger_argument(channel, globals.client, data.context, topic=data.topic))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class BipolarTriggerRequest(BaseModel):
|
|||||||
channel_id: str # String to handle large Discord IDs from JS
|
channel_id: str # String to handle large Discord IDs from JS
|
||||||
message_id: str = None # Optional: starting message ID (string)
|
message_id: str = None # Optional: starting message ID (string)
|
||||||
context: str = ""
|
context: str = ""
|
||||||
|
topic: str = "" # Optional: custom argument topic (overrides random topic selection)
|
||||||
|
|
||||||
|
|
||||||
class ManualCropRequest(BaseModel):
|
class ManualCropRequest(BaseModel):
|
||||||
|
|||||||
@@ -238,6 +238,14 @@
|
|||||||
<input type="text" id="bipolar-context" placeholder="e.g., They're fighting about who's the real Miku..." style="width: 100%; margin-top: 0.3rem;">
|
<input type="text" id="bipolar-context" placeholder="e.g., They're fighting about who's the real Miku..." style="width: 100%; margin-top: 0.3rem;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 1rem;">
|
||||||
|
<label for="bipolar-topic">Argument Topic (optional — overrides random topic):</label>
|
||||||
|
<input type="text" id="bipolar-topic" placeholder="e.g., Who deserves the spotlight? A philosophical debate about worth..." style="width: 100%; margin-top: 0.3rem;">
|
||||||
|
<div style="font-size: 0.75rem; color: #777; margin-top: 0.2rem;">
|
||||||
|
Leave blank for random topic selection. Enter a custom theme to frame the argument uniquely.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button onclick="triggerBipolarArgument()" style="background: #9932CC; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer;">
|
<button onclick="triggerBipolarArgument()" style="background: #9932CC; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer;">
|
||||||
⚔️ Trigger Argument
|
⚔️ Trigger Argument
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ async function triggerBipolarArgument() {
|
|||||||
const channelIdInput = document.getElementById('bipolar-channel-id').value.trim();
|
const channelIdInput = document.getElementById('bipolar-channel-id').value.trim();
|
||||||
const messageIdInput = document.getElementById('bipolar-message-id').value.trim();
|
const messageIdInput = document.getElementById('bipolar-message-id').value.trim();
|
||||||
const context = document.getElementById('bipolar-context').value;
|
const context = document.getElementById('bipolar-context').value;
|
||||||
|
const topic = document.getElementById('bipolar-topic').value.trim();
|
||||||
const statusDiv = document.getElementById('bipolar-status');
|
const statusDiv = document.getElementById('bipolar-status');
|
||||||
|
|
||||||
if (!channelIdInput) {
|
if (!channelIdInput) {
|
||||||
@@ -278,6 +279,10 @@ async function triggerBipolarArgument() {
|
|||||||
requestBody.message_id = messageIdInput;
|
requestBody.message_id = messageIdInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (topic) {
|
||||||
|
requestBody.topic = topic;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await apiCall('/bipolar-mode/trigger-argument', 'POST', requestBody);
|
const result = await apiCall('/bipolar-mode/trigger-argument', 'POST', requestBody);
|
||||||
|
|
||||||
if (result.status === 'error') {
|
if (result.status === 'error') {
|
||||||
@@ -290,6 +295,7 @@ async function triggerBipolarArgument() {
|
|||||||
showNotification(`⚔️ Argument triggered!`);
|
showNotification(`⚔️ Argument triggered!`);
|
||||||
|
|
||||||
document.getElementById('bipolar-context').value = '';
|
document.getElementById('bipolar-context').value = '';
|
||||||
|
document.getElementById('bipolar-topic').value = '';
|
||||||
document.getElementById('bipolar-message-id').value = '';
|
document.getElementById('bipolar-message-id').value = '';
|
||||||
|
|
||||||
loadActiveArguments();
|
loadActiveArguments();
|
||||||
|
|||||||
@@ -1175,7 +1175,7 @@ def should_end_argument(channel_id: int) -> tuple:
|
|||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
async def run_argument(channel: discord.TextChannel, client, trigger_context: str = "", starting_message: discord.Message = None):
|
async def run_argument(channel: discord.TextChannel, client, trigger_context: str = "", starting_message: discord.Message = None, argument_topic: str = None):
|
||||||
"""Run a full argument event between both Mikus
|
"""Run a full argument event between both Mikus
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1184,6 +1184,9 @@ async def run_argument(channel: discord.TextChannel, client, trigger_context: st
|
|||||||
trigger_context: Optional context about what triggered the argument
|
trigger_context: Optional context about what triggered the argument
|
||||||
starting_message: Optional message to use as the first message in the argument
|
starting_message: Optional message to use as the first message in the argument
|
||||||
(the opposite persona will respond to it)
|
(the opposite persona will respond to it)
|
||||||
|
argument_topic: Optional custom topic string. If provided, overrides the
|
||||||
|
random topic selection. Pass empty string to force no topic.
|
||||||
|
Pass None (default) to use random topic selection.
|
||||||
"""
|
"""
|
||||||
from utils.llm import query_llama
|
from utils.llm import query_llama
|
||||||
from utils.conversation_history import conversation_history
|
from utils.conversation_history import conversation_history
|
||||||
@@ -1225,8 +1228,19 @@ async def run_argument(channel: discord.TextChannel, client, trigger_context: st
|
|||||||
conversation_log = []
|
conversation_log = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Pick a dynamic argument topic to give this argument a unique framing
|
# Pick a dynamic argument topic to give this argument a unique framing.
|
||||||
|
# If caller provided a custom topic, use it instead of random selection.
|
||||||
|
# If caller passed empty string, use no topic (no theme guidance).
|
||||||
|
if argument_topic is None:
|
||||||
|
# Default: random weighted selection from rotation system
|
||||||
argument_topic = pick_argument_topic(channel_id)
|
argument_topic = pick_argument_topic(channel_id)
|
||||||
|
elif argument_topic == "":
|
||||||
|
# Explicitly no topic — skip theme guidance entirely
|
||||||
|
argument_topic = ""
|
||||||
|
logger.info(f"No argument topic requested for channel {channel_id}")
|
||||||
|
else:
|
||||||
|
# Custom topic from caller (e.g. Web UI field)
|
||||||
|
logger.info(f"Using custom argument topic for channel {channel_id}: '{argument_topic[:80]}...'")
|
||||||
|
|
||||||
# If no starting message, generate the initial interrupting message
|
# If no starting message, generate the initial interrupting message
|
||||||
if last_message is None:
|
if last_message is None:
|
||||||
@@ -1524,7 +1538,7 @@ async def maybe_trigger_argument(channel: discord.TextChannel, client, context:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def force_trigger_argument(channel: discord.TextChannel, client, context: str = "", starting_message: discord.Message = None):
|
async def force_trigger_argument(channel: discord.TextChannel, client, context: str = "", starting_message: discord.Message = None, topic: str = None):
|
||||||
"""Force trigger an argument (for manual triggers)
|
"""Force trigger an argument (for manual triggers)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1532,6 +1546,7 @@ async def force_trigger_argument(channel: discord.TextChannel, client, context:
|
|||||||
client: Discord client
|
client: Discord client
|
||||||
context: Optional context string
|
context: Optional context string
|
||||||
starting_message: Optional message to use as the first message in the argument
|
starting_message: Optional message to use as the first message in the argument
|
||||||
|
topic: Optional custom argument topic. None = random, "" = no topic, str = custom.
|
||||||
"""
|
"""
|
||||||
if not globals.BIPOLAR_MODE:
|
if not globals.BIPOLAR_MODE:
|
||||||
logger.warning("Cannot trigger argument - bipolar mode is not enabled")
|
logger.warning("Cannot trigger argument - bipolar mode is not enabled")
|
||||||
@@ -1541,11 +1556,11 @@ async def force_trigger_argument(channel: discord.TextChannel, client, context:
|
|||||||
logger.warning("Argument already in progress in this channel")
|
logger.warning("Argument already in progress in this channel")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
create_tracked_task(run_argument(channel, client, context, starting_message), task_name="bipolar_argument_forced")
|
create_tracked_task(run_argument(channel, client, context, starting_message, argument_topic=topic), task_name="bipolar_argument_forced")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def force_trigger_argument_from_message_id(channel_id: int, message_id: int, client, context: str = ""):
|
async def force_trigger_argument_from_message_id(channel_id: int, message_id: int, client, context: str = "", topic: str = None):
|
||||||
"""Force trigger an argument starting from a specific message ID
|
"""Force trigger an argument starting from a specific message ID
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1579,5 +1594,5 @@ async def force_trigger_argument_from_message_id(channel_id: int, message_id: in
|
|||||||
return False, f"Failed to fetch message: {str(e)}"
|
return False, f"Failed to fetch message: {str(e)}"
|
||||||
|
|
||||||
# Trigger the argument with this message as starting point
|
# Trigger the argument with this message as starting point
|
||||||
create_tracked_task(run_argument(channel, client, context, message), task_name="bipolar_argument_from_msg")
|
create_tracked_task(run_argument(channel, client, context, message, argument_topic=topic), task_name="bipolar_argument_from_msg")
|
||||||
return True, None
|
return True, None
|
||||||
|
|||||||
Reference in New Issue
Block a user