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
5 changes: 5 additions & 0 deletions astrbot/core/agent/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class CheckpointData(BaseModel):
class Message(BaseModel):
"""A message in a conversation."""

id: str | None = None
"""The provider-specific message ID."""

role: Literal[
"system",
"user",
Expand Down Expand Up @@ -239,6 +242,8 @@ def check_content_required(self):
@model_serializer(mode="wrap")
def serialize(self, handler):
data = handler(self)
if self.id is None:
data.pop("id", None)
if self.tool_calls is None:
data.pop("tool_calls", None)
if self.tool_call_id is None:
Expand Down
9 changes: 7 additions & 2 deletions astrbot/core/agent/runners/tool_loop_agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ async def _complete_with_assistant_response(self, llm_resp: LLMResponse) -> None
parts.append(TextPart(text=llm_resp.completion_text))
if len(parts) == 0:
logger.warning("LLM returned empty assistant message with no tool calls.")
self.run_context.messages.append(Message(role="assistant", content=parts))
self.run_context.messages.append(
Message(role="assistant", content=parts, id=llm_resp.message_id)
)

try:
await self.agent_hooks.on_agent_done(self.run_context, llm_resp)
Expand Down Expand Up @@ -899,6 +901,7 @@ async def step(self):
tool_calls_info=AssistantMessageSegment(
tool_calls=llm_resp.to_openai_to_calls_model(),
content=parts,
id=llm_resp.message_id,
),
tool_calls_result=tool_call_result_blocks,
)
Expand Down Expand Up @@ -1386,7 +1389,9 @@ async def _finalize_aborted_step(
if llm_resp.completion_text:
parts.append(TextPart(text=llm_resp.completion_text))
if parts:
self.run_context.messages.append(Message(role="assistant", content=parts))
self.run_context.messages.append(
Message(role="assistant", content=parts, id=llm_resp.message_id)
)

try:
await self.agent_hooks.on_agent_done(self.run_context, llm_resp)
Expand Down
12 changes: 12 additions & 0 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,18 @@
"proxy": "",
"custom_headers": {},
},
"OpenAI Responses": {
"id": "openai_responses",
"provider": "openai",
"type": "openai_responses",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://api.openai.com/v1",
"timeout": 120,
"proxy": "",
"custom_headers": {},
},
"Google Gemini": {
"id": "google_gemini",
"provider": "google",
Expand Down
5 changes: 5 additions & 0 deletions astrbot/core/provider/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ class LLMResponse:

id: str | None = None
"""The ID of the response. For chunked responses, it's the ID of the chunk; for non-chunked responses, it's the ID of the response."""
message_id: str | None = None
"""The ID of the assistant message output item, if the provider returns one."""
usage: TokenUsage | None = None
"""The usage of the response. For chunked responses, it's the usage of the chunk; for non-chunked responses, it's the usage of the response."""

Expand All @@ -343,6 +345,7 @@ def __init__(
| None = None,
is_chunk: bool = False,
id: str | None = None,
message_id: str | None = None,
usage: TokenUsage | None = None,
) -> None:
"""初始化 LLMResponse
Expand Down Expand Up @@ -379,6 +382,8 @@ def __init__(

if id is not None:
self.id = id
if message_id is not None:
self.message_id = message_id
if usage is not None:
self.usage = usage

Expand Down
4 changes: 4 additions & 0 deletions astrbot/core/provider/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ def dynamic_import_provider(self, type: str) -> None:
from .sources.openai_source import (
ProviderOpenAIOfficial as ProviderOpenAIOfficial,
)
case "openai_responses":
from .sources.openai_responses_source import (
ProviderOpenAIResponses as ProviderOpenAIResponses,
)
case "longcat_chat_completion":
from .sources.longcat_source import ProviderLongCat as ProviderLongCat
case "minimax_token_plan":
Expand Down
Loading