mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 15:37:55 +08:00
core/agent: todo review nag as developer role, not fake user
Internal control uses DeveloperChatMessage so retry/history treat it as non-human. Responses bridge maps developer mid-turn; compact/history label it.
This commit is contained in:
@@ -8,6 +8,7 @@ from plyngent.lmproto.openai_compatible.model import (
|
||||
AssistantChatMessage,
|
||||
AssistantFunctionToolCall,
|
||||
ChatCompletionsParam,
|
||||
DeveloperChatMessage,
|
||||
SystemChatMessage,
|
||||
ToolChatMessage,
|
||||
UserChatMessage,
|
||||
@@ -43,12 +44,14 @@ _SEED_MESSAGE_TEMPLATE = (
|
||||
)
|
||||
|
||||
|
||||
def format_transcript(messages: Sequence[AnyChatMessage]) -> str:
|
||||
def format_transcript(messages: Sequence[AnyChatMessage]) -> str: # noqa: C901
|
||||
"""Render messages as plain text for a summarization prompt."""
|
||||
lines: list[str] = []
|
||||
for msg in messages:
|
||||
if isinstance(msg, SystemChatMessage):
|
||||
lines.append(f"[system] {msg.content}")
|
||||
elif isinstance(msg, DeveloperChatMessage):
|
||||
lines.append(f"[developer] {msg.content}")
|
||||
elif isinstance(msg, UserChatMessage):
|
||||
lines.append(f"[user] {msg.content}")
|
||||
elif isinstance(msg, AssistantChatMessage):
|
||||
|
||||
@@ -13,10 +13,10 @@ from plyngent.lmproto.openai_compatible.model import (
|
||||
AssistantChatMessage,
|
||||
AssistantFunctionToolCall,
|
||||
ChatCompletionsParam,
|
||||
DeveloperChatMessage,
|
||||
StreamOptions,
|
||||
StreamToolCallDelta,
|
||||
ToolChatMessage,
|
||||
UserChatMessage,
|
||||
)
|
||||
from plyngent.typedef import Unset # noqa: TC001
|
||||
|
||||
@@ -377,7 +377,8 @@ async def run_chat_loop(
|
||||
and not todo_review_injected
|
||||
):
|
||||
todo_review_injected = True
|
||||
messages.append(UserChatMessage(content=todo_stack.review_prompt()))
|
||||
# Non-user control identity so retry/history don't treat this as human input.
|
||||
messages.append(DeveloperChatMessage(content=todo_stack.review_prompt()))
|
||||
continue
|
||||
return
|
||||
async for event in _execute_tool_calls(
|
||||
|
||||
@@ -29,6 +29,7 @@ from plyngent.lmproto.openai_compatible.model import (
|
||||
ChatCompletionsParam,
|
||||
ChunkChoice,
|
||||
DeltaMessage,
|
||||
DeveloperChatMessage,
|
||||
StreamFunctionDelta,
|
||||
StreamToolCallDelta,
|
||||
SystemChatMessage,
|
||||
@@ -97,6 +98,10 @@ def chat_messages_to_responses_input(
|
||||
if isinstance(message, SystemChatMessage):
|
||||
if message.content.strip():
|
||||
instructions_parts.append(message.content)
|
||||
elif isinstance(message, DeveloperChatMessage):
|
||||
# Keep mid-turn control as input developer messages (not folded into instructions).
|
||||
if message.content.strip():
|
||||
items.append(ResponseEasyInputMessage(role="developer", content=message.content))
|
||||
elif isinstance(message, UserChatMessage):
|
||||
items.append(ResponseEasyInputMessage(role="user", content=message.content))
|
||||
elif isinstance(message, AssistantChatMessage):
|
||||
|
||||
@@ -99,9 +99,9 @@ class TodoStack:
|
||||
return "\n".join(lines)
|
||||
|
||||
def review_prompt(self) -> str:
|
||||
"""User-message body injected when the model finishes without todo ops."""
|
||||
"""Control-message body (developer role) when the model finishes without todo ops."""
|
||||
return (
|
||||
"[system: todo stack review]\n"
|
||||
"Todo stack review (internal control — not a human message).\n"
|
||||
"Open sub-tasks remain and you did not call any todo_* tools this turn.\n"
|
||||
"Review the stack: mark finished items done, update in_progress, "
|
||||
"push new sub-tasks, or pop/cancel obsolete ones. Then continue the user work.\n\n"
|
||||
|
||||
@@ -15,6 +15,8 @@ from plyngent.cli.selection import select_model, select_provider
|
||||
from plyngent.lmproto.openai_compatible.model import (
|
||||
AssistantChatMessage,
|
||||
AssistantFunctionToolCall,
|
||||
DeveloperChatMessage,
|
||||
SystemChatMessage,
|
||||
ToolChatMessage,
|
||||
UserChatMessage,
|
||||
)
|
||||
@@ -973,6 +975,10 @@ def _preview_content(text: str | None) -> str:
|
||||
def _format_history_message(index: int, message: AnyChatMessage) -> str:
|
||||
if isinstance(message, UserChatMessage):
|
||||
return f"{index}. user: {_preview_content(message.content)}"
|
||||
if isinstance(message, DeveloperChatMessage):
|
||||
return f"{index}. developer: {_preview_content(message.content)}"
|
||||
if isinstance(message, SystemChatMessage):
|
||||
return f"{index}. system: {_preview_content(message.content)}"
|
||||
if isinstance(message, AssistantChatMessage):
|
||||
parts: list[str] = []
|
||||
if isinstance(message.content, str) and message.content:
|
||||
|
||||
Reference in New Issue
Block a user