feat(backends,conversations,frontends): opus 5.5 progress updates reach telegram, current claude cli in the image
This commit is contained in:
1 parent
e58153628c
commit
bbce425eeb
9 files changed
+140
-20
No files matched your search
@@ -24,6 +24,9 @@ class ClaudeOptions(BaseModel):
|
||||
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
|
||||
|
||||
effort: str | None = None
|
||||
thinking: Mapping[str, str] | None = None
|
||||
"""Thinking config for the SDK; ``None`` keeps the CLI default."""
|
||||
|
||||
include_partial_messages: bool = True
|
||||
"""Token-level deltas on the wire; off means one delta per finished block."""
|
||||
|
||||
|
||||
@@ -726,6 +726,7 @@ class ClaudeSdkBackend:
|
||||
return ClaudeAgentOptions(
|
||||
model=agent.model or None,
|
||||
effort=cast("Any", opt.effort),
|
||||
thinking=cast("Any", dict(opt.thinking) if opt.thinking else None),
|
||||
system_prompt=system_prompt,
|
||||
setting_sources=[],
|
||||
strict_mcp_config=True,
|
||||
@@ -933,7 +934,17 @@ async def _disconnect(live: Session) -> None:
|
||||
_log.exception("disconnect failed for session %s", live.session_id)
|
||||
|
||||
|
||||
CLI_ENV = "BEAVER_CLAUDE_CLI"
|
||||
"""Path to a ``claude`` CLI that wins over the one bundled in the wheel."""
|
||||
|
||||
|
||||
def _claude_binary() -> str:
|
||||
override = os.environ.get(CLI_ENV)
|
||||
if override:
|
||||
if not Path(override).is_file():
|
||||
msg = f"{CLI_ENV} points at {override!r}, which is not a file"
|
||||
raise FileNotFoundError(msg)
|
||||
return override
|
||||
bundled = Path(claude_agent_sdk.__file__).parent / "_bundled" / "claude"
|
||||
if bundled.is_file():
|
||||
return str(bundled)
|
||||
|
||||
@@ -32,11 +32,11 @@ from beaver_gateway.frontends.accumulate import StreamAccumulator
|
||||
from beaver_gateway.storage.models import Conversation, InjectQueueItem, RateLimit
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import AsyncIterator, Callable, Sequence
|
||||
from collections.abc import AsyncIterator, Callable, Iterable, Sequence
|
||||
|
||||
from beaver_gateway.events.stream import MessageStreamEvent
|
||||
|
||||
__all__ = ["Turns", "content_of", "image_block"]
|
||||
__all__ = ["Turns", "content_of", "image_block", "reply_text"]
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
@@ -164,12 +164,7 @@ class Turns(Seeds):
|
||||
):
|
||||
acc.feed(event)
|
||||
message = acc.finalize(model=agent.model)
|
||||
reply = "\n\n".join(
|
||||
getattr(b, "text", "")
|
||||
for b in message.content
|
||||
if getattr(b, "type", "") == "text"
|
||||
).strip()
|
||||
return reply, capture
|
||||
return reply_text(message.content), capture
|
||||
|
||||
async def before_turn(self, conv: Conversation) -> str | None: # noqa: ARG002
|
||||
return None
|
||||
@@ -489,6 +484,22 @@ class Turns(Seeds):
|
||||
IMAGE_MAX_BYTES = 5 * 1024 * 1024
|
||||
|
||||
|
||||
def reply_text(content: Iterable[Any]) -> str:
|
||||
"""Text blocks plus non-empty thinking blocks, in block order.
|
||||
|
||||
What the model writes between tool calls rides in thinking blocks; the
|
||||
hidden reasoning in them is empty and drops out on its own.
|
||||
"""
|
||||
parts: list[str] = []
|
||||
for block in content:
|
||||
kind = getattr(block, "type", "")
|
||||
if kind == "text":
|
||||
parts.append(getattr(block, "text", "") or "")
|
||||
elif kind == "thinking":
|
||||
parts.append(getattr(block, "thinking", "") or "")
|
||||
return "\n\n".join(p for p in parts if p).strip()
|
||||
|
||||
|
||||
def content_of(
|
||||
text: str, attachments: Sequence[dict[str, Any]] | None
|
||||
) -> str | list[dict[str, Any]]:
|
||||
|
||||
@@ -983,7 +983,12 @@ class TelegramFrontend(Frontend):
|
||||
draft.set_status(self.texts.writing)
|
||||
draft.append(str(delta.get("text") or ""))
|
||||
elif delta.get("type") == "thinking_delta":
|
||||
draft.set_status(self.texts.thinking)
|
||||
progress = str(delta.get("thinking") or "")
|
||||
if progress:
|
||||
draft.set_status(self.texts.writing)
|
||||
draft.append(progress)
|
||||
else:
|
||||
draft.set_status(self.texts.thinking)
|
||||
elif kind == "content_block_start":
|
||||
block = raw.get("content_block") or {}
|
||||
if block.get("type") == "tool_use":
|
||||
|
||||
Reference in new issue
Block a user