mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-09 08:57:55 +08:00
core/agent: share stream chunk builders across responses and messages bridges
This commit is contained in:
@@ -30,14 +30,9 @@ from plyngent.lmproto.openai_compatible.model import (
|
|||||||
AssistantFunctionTool,
|
AssistantFunctionTool,
|
||||||
AssistantFunctionToolCall,
|
AssistantFunctionToolCall,
|
||||||
ChatCompletionChoice,
|
ChatCompletionChoice,
|
||||||
ChatCompletionChunk,
|
|
||||||
ChatCompletionResponse,
|
ChatCompletionResponse,
|
||||||
ChatCompletionsParam,
|
ChatCompletionsParam,
|
||||||
ChunkChoice,
|
|
||||||
DeltaMessage,
|
|
||||||
DeveloperChatMessage,
|
DeveloperChatMessage,
|
||||||
StreamFunctionDelta,
|
|
||||||
StreamToolCallDelta,
|
|
||||||
SystemChatMessage,
|
SystemChatMessage,
|
||||||
ToolFunctionItem,
|
ToolFunctionItem,
|
||||||
UserChatMessage,
|
UserChatMessage,
|
||||||
@@ -290,88 +285,6 @@ def anthropic_response_to_chat_completion(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def finish_reason_chunk(
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
finish_reason: str,
|
|
||||||
created: int = 0,
|
|
||||||
) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="anthropic-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(),
|
|
||||||
finish_reason=cast("Any", finish_reason),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def text_delta_chunk(*, model: str, content: str, created: int = 0) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="anthropic-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(content=content),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def tool_call_delta_chunk(
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
index: int,
|
|
||||||
call_id: str | None = None,
|
|
||||||
name: str | None = None,
|
|
||||||
arguments: str | None = None,
|
|
||||||
created: int = 0,
|
|
||||||
) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="anthropic-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(
|
|
||||||
tool_calls=[
|
|
||||||
StreamToolCallDelta(
|
|
||||||
index=index,
|
|
||||||
id=call_id if call_id is not None else UNSET,
|
|
||||||
type="function",
|
|
||||||
function=StreamFunctionDelta(
|
|
||||||
name=name if name is not None else UNSET,
|
|
||||||
arguments=arguments if arguments is not None else UNSET,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def usage_chunk(*, model: str, usage: dict[str, Any], created: int = 0) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="anthropic-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[],
|
|
||||||
usage=usage,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _max_tokens_from_param(param: ChatCompletionsParam) -> int:
|
def _max_tokens_from_param(param: ChatCompletionsParam) -> int:
|
||||||
if param.max_completion_tokens is not UNSET:
|
if param.max_completion_tokens is not UNSET:
|
||||||
return int(param.max_completion_tokens)
|
return int(param.max_completion_tokens)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ from plyngent.agent.messages_bridge import (
|
|||||||
anthropic_stop_to_finish_reason,
|
anthropic_stop_to_finish_reason,
|
||||||
anthropic_usage_to_dict,
|
anthropic_usage_to_dict,
|
||||||
chat_param_to_anthropic_param,
|
chat_param_to_anthropic_param,
|
||||||
|
)
|
||||||
|
from plyngent.agent.stream_chunks import (
|
||||||
finish_reason_chunk,
|
finish_reason_chunk,
|
||||||
text_delta_chunk,
|
text_delta_chunk,
|
||||||
tool_call_delta_chunk,
|
tool_call_delta_chunk,
|
||||||
|
|||||||
@@ -27,11 +27,7 @@ from plyngent.lmproto.openai_compatible.model import (
|
|||||||
ChatCompletionChunk,
|
ChatCompletionChunk,
|
||||||
ChatCompletionResponse,
|
ChatCompletionResponse,
|
||||||
ChatCompletionsParam,
|
ChatCompletionsParam,
|
||||||
ChunkChoice,
|
|
||||||
DeltaMessage,
|
|
||||||
DeveloperChatMessage,
|
DeveloperChatMessage,
|
||||||
StreamFunctionDelta,
|
|
||||||
StreamToolCallDelta,
|
|
||||||
SystemChatMessage,
|
SystemChatMessage,
|
||||||
ToolFunctionItem,
|
ToolFunctionItem,
|
||||||
UserChatMessage,
|
UserChatMessage,
|
||||||
@@ -204,58 +200,6 @@ def response_to_chat_completion(response: Response) -> ChatCompletionResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def finish_reason_chunk(
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
finish_reason: str,
|
|
||||||
created: int = 0,
|
|
||||||
) -> ChatCompletionChunk:
|
|
||||||
"""Terminal stream chunk carrying only ``finish_reason`` (no delta text)."""
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="resp-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(),
|
|
||||||
finish_reason=cast("Any", finish_reason),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def text_delta_chunk(*, model: str, content: str, created: int = 0) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="resp-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(content=content),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def reasoning_delta_chunk(*, model: str, content: str, created: int = 0) -> ChatCompletionChunk:
|
|
||||||
return ChatCompletionChunk(
|
|
||||||
id="resp-stream",
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(reasoning_content=content),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def tool_call_chunks_from_response(
|
def tool_call_chunks_from_response(
|
||||||
response: Response,
|
response: Response,
|
||||||
*,
|
*,
|
||||||
@@ -263,50 +207,28 @@ def tool_call_chunks_from_response(
|
|||||||
created: int = 0,
|
created: int = 0,
|
||||||
) -> list[ChatCompletionChunk]:
|
) -> list[ChatCompletionChunk]:
|
||||||
"""Emit complete tool-call stream deltas (one chunk per call) for loop merge."""
|
"""Emit complete tool-call stream deltas (one chunk per call) for loop merge."""
|
||||||
|
from .stream_chunks import tool_call_delta_chunk
|
||||||
|
|
||||||
calls = response_function_calls(response)
|
calls = response_function_calls(response)
|
||||||
chunks: list[ChatCompletionChunk] = []
|
return [
|
||||||
for index, call in enumerate(calls):
|
tool_call_delta_chunk(
|
||||||
chunks.append(
|
model=model,
|
||||||
ChatCompletionChunk(
|
index=index,
|
||||||
id=response.id,
|
call_id=call.call_id,
|
||||||
object="chat.completion.chunk",
|
name=call.name,
|
||||||
created=created,
|
arguments=call.arguments,
|
||||||
model=model,
|
created=created,
|
||||||
choices=[
|
|
||||||
ChunkChoice(
|
|
||||||
index=0,
|
|
||||||
delta=DeltaMessage(
|
|
||||||
tool_calls=[
|
|
||||||
StreamToolCallDelta(
|
|
||||||
index=index,
|
|
||||||
id=call.call_id,
|
|
||||||
type="function",
|
|
||||||
function=StreamFunctionDelta(
|
|
||||||
name=call.name,
|
|
||||||
arguments=call.arguments,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return chunks
|
for index, call in enumerate(calls)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def usage_chunk_from_response(response: Response, *, model: str) -> ChatCompletionChunk | None:
|
def usage_chunk_from_response(response: Response, *, model: str) -> ChatCompletionChunk | None:
|
||||||
|
from .stream_chunks import usage_chunk
|
||||||
|
|
||||||
if response.usage is UNSET or response.usage is None:
|
if response.usage is UNSET or response.usage is None:
|
||||||
return None
|
return None
|
||||||
created = int(response.created_at)
|
return usage_chunk(model=model, usage=response.usage, created=int(response.created_at))
|
||||||
return ChatCompletionChunk(
|
|
||||||
id=response.id,
|
|
||||||
object="chat.completion.chunk",
|
|
||||||
created=created,
|
|
||||||
model=model,
|
|
||||||
choices=[],
|
|
||||||
usage=response.usage,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _merge_response_tools(
|
def _merge_response_tools(
|
||||||
|
|||||||
@@ -16,15 +16,17 @@ from msgspec import UNSET
|
|||||||
|
|
||||||
from plyngent.agent.responses_bridge import (
|
from plyngent.agent.responses_bridge import (
|
||||||
chat_param_to_responses_kwargs,
|
chat_param_to_responses_kwargs,
|
||||||
finish_reason_chunk,
|
|
||||||
reasoning_delta_chunk,
|
|
||||||
response_to_assistant_message,
|
response_to_assistant_message,
|
||||||
response_to_chat_completion,
|
response_to_chat_completion,
|
||||||
responses_status_to_finish_reason,
|
responses_status_to_finish_reason,
|
||||||
text_delta_chunk,
|
|
||||||
tool_call_chunks_from_response,
|
tool_call_chunks_from_response,
|
||||||
usage_chunk_from_response,
|
usage_chunk_from_response,
|
||||||
)
|
)
|
||||||
|
from plyngent.agent.stream_chunks import (
|
||||||
|
finish_reason_chunk,
|
||||||
|
reasoning_delta_chunk,
|
||||||
|
text_delta_chunk,
|
||||||
|
)
|
||||||
from plyngent.lmproto.openai.model import Response as ResponseModel
|
from plyngent.lmproto.openai.model import Response as ResponseModel
|
||||||
from plyngent.lmproto.openai.model import ResponsesCreateParam
|
from plyngent.lmproto.openai.model import ResponsesCreateParam
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
"""Shared chat-completions stream chunk builders for protocol bridges.
|
||||||
|
|
||||||
|
Both the Responses bridge and the Anthropic Messages bridge synthesize
|
||||||
|
``ChatCompletionChunk`` objects so the agent loop can consume provider streams
|
||||||
|
uniformly. The generic builders below are provider-agnostic; provider bridges
|
||||||
|
map their events onto them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
|
from msgspec import UNSET
|
||||||
|
|
||||||
|
from plyngent.lmproto.openai_compatible.model import (
|
||||||
|
ChatCompletionChunk,
|
||||||
|
ChunkChoice,
|
||||||
|
DeltaMessage,
|
||||||
|
StreamFunctionDelta,
|
||||||
|
StreamToolCallDelta,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def finish_reason_chunk(
|
||||||
|
*,
|
||||||
|
model: str,
|
||||||
|
finish_reason: str,
|
||||||
|
created: int = 0,
|
||||||
|
) -> ChatCompletionChunk:
|
||||||
|
"""Terminal stream chunk carrying only ``finish_reason`` (no delta text)."""
|
||||||
|
return ChatCompletionChunk(
|
||||||
|
id="bridge-stream",
|
||||||
|
object="chat.completion.chunk",
|
||||||
|
created=created,
|
||||||
|
model=model,
|
||||||
|
choices=[
|
||||||
|
ChunkChoice(
|
||||||
|
index=0,
|
||||||
|
delta=DeltaMessage(),
|
||||||
|
finish_reason=cast("Any", finish_reason),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def text_delta_chunk(*, model: str, content: str, created: int = 0) -> ChatCompletionChunk:
|
||||||
|
return ChatCompletionChunk(
|
||||||
|
id="bridge-stream",
|
||||||
|
object="chat.completion.chunk",
|
||||||
|
created=created,
|
||||||
|
model=model,
|
||||||
|
choices=[
|
||||||
|
ChunkChoice(
|
||||||
|
index=0,
|
||||||
|
delta=DeltaMessage(content=content),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reasoning_delta_chunk(*, model: str, content: str, created: int = 0) -> ChatCompletionChunk:
|
||||||
|
return ChatCompletionChunk(
|
||||||
|
id="bridge-stream",
|
||||||
|
object="chat.completion.chunk",
|
||||||
|
created=created,
|
||||||
|
model=model,
|
||||||
|
choices=[
|
||||||
|
ChunkChoice(
|
||||||
|
index=0,
|
||||||
|
delta=DeltaMessage(reasoning_content=content),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def tool_call_delta_chunk(
|
||||||
|
*,
|
||||||
|
model: str,
|
||||||
|
index: int,
|
||||||
|
call_id: str | None = None,
|
||||||
|
name: str | None = None,
|
||||||
|
arguments: str | None = None,
|
||||||
|
created: int = 0,
|
||||||
|
) -> ChatCompletionChunk:
|
||||||
|
return ChatCompletionChunk(
|
||||||
|
id="bridge-stream",
|
||||||
|
object="chat.completion.chunk",
|
||||||
|
created=created,
|
||||||
|
model=model,
|
||||||
|
choices=[
|
||||||
|
ChunkChoice(
|
||||||
|
index=0,
|
||||||
|
delta=DeltaMessage(
|
||||||
|
tool_calls=[
|
||||||
|
StreamToolCallDelta(
|
||||||
|
index=index,
|
||||||
|
id=call_id if call_id is not None else UNSET,
|
||||||
|
type="function",
|
||||||
|
function=StreamFunctionDelta(
|
||||||
|
name=name if name is not None else UNSET,
|
||||||
|
arguments=arguments if arguments is not None else UNSET,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def usage_chunk(*, model: str, usage: dict[str, Any], created: int = 0) -> ChatCompletionChunk:
|
||||||
|
return ChatCompletionChunk(
|
||||||
|
id="bridge-stream",
|
||||||
|
object="chat.completion.chunk",
|
||||||
|
created=created,
|
||||||
|
model=model,
|
||||||
|
choices=[],
|
||||||
|
usage=usage,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"finish_reason_chunk",
|
||||||
|
"reasoning_delta_chunk",
|
||||||
|
"text_delta_chunk",
|
||||||
|
"tool_call_delta_chunk",
|
||||||
|
"usage_chunk",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user