refactor: remove per-followup result attribution

This commit is contained in:
_Kerman
2026-07-30 16:48:28 +08:00
parent f6db60b52c
commit a6baddaaac
72 changed files with 586 additions and 1059 deletions

View File

@@ -1,4 +1,4 @@
from .api import DeepSeekHarness, DeepSeekHarnessConfig, Session, TurnResult
from .api import DeepSeekHarness, DeepSeekHarnessConfig, RunResult, Session
from .client import HarnessClient, HarnessConfig
from .models import IncomingRequest, InitializeResponse, JsonObject, Notification, ServerInfo
@@ -6,7 +6,7 @@ __all__ = [
"DeepSeekHarness",
"DeepSeekHarnessConfig",
"Session",
"TurnResult",
"RunResult",
"HarnessClient",
"HarnessConfig",
"IncomingRequest",

View File

@@ -35,9 +35,8 @@ class DeepSeekHarnessConfig:
@dataclass(slots=True)
class TurnResult:
class RunResult:
session_id: str
status: str
final_response: str
events: list[JsonObject]
notifications: list[Notification]
@@ -119,7 +118,7 @@ class DeepSeekHarness:
*,
session_id: str | None = None,
on_notification: Callable[[Notification], None] | None = None,
) -> TurnResult:
) -> RunResult:
return self.start_session(session_id).run(input, on_notification=on_notification)
@@ -133,15 +132,12 @@ class Session:
input: str | list[JsonObject],
*,
on_notification: Callable[[Notification], None] | None = None,
) -> TurnResult:
) -> RunResult:
content_blocks = normalize_input(input)
notifications: list[Notification] = []
events: list[JsonObject] = []
status = "error"
finished = False
def collect(notification: Notification) -> None:
nonlocal finished, status
notifications.append(notification)
if on_notification is not None:
on_notification(notification)
@@ -152,25 +148,31 @@ class Session:
event = notification.payload.get("event")
if isinstance(event, dict):
events.append(event)
if notification.method == "session.finished" and notification.payload.get("sessionId") == self.id:
status = str(notification.payload.get("status") or "ok")
finished = True
with self.harness.client.subscribe_session_notifications(self.id) as subscription:
self.harness.client.session_prompt(
message_id = self.harness.client.session_prompt(
self.id,
content_blocks,
on_notification=collect,
notification_subscription=subscription,
)
while not finished:
received = False
while True:
notification = subscription.next()
if not received:
if not _is_inbox_receipt(notification, self.id, message_id):
continue
received = True
collect(notification)
if (
notification.method == "session.status"
and notification.payload.get("sessionId") == self.id
and notification.payload.get("status") == "idle"
):
break
return TurnResult(
return RunResult(
session_id=self.id,
status=status,
final_response=final_response(events),
events=events,
notifications=notifications,
@@ -178,6 +180,19 @@ class Session:
)
def _is_inbox_receipt(notification: Notification, session_id: str, message_id: str) -> bool:
if notification.method != "session.event" or notification.payload.get("sessionId") != session_id:
return False
event = notification.payload.get("event")
if not isinstance(event, dict) or event.get("type") != "agent/inbox/spliced":
return False
data = event.get("data")
inserted = data.get("inserted") if isinstance(data, dict) else None
return isinstance(inserted, list) and any(
isinstance(message, dict) and message.get("id") == message_id for message in inserted
)
def normalize_input(input: str | list[JsonObject]) -> list[JsonObject]:
if isinstance(input, str):
return [{"type": "text", "text": input}]

View File

@@ -10,7 +10,7 @@ import uuid
from collections import deque
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, Literal, TypeAlias, TypeVar
from typing import Callable, TypeAlias, TypeVar
from pydantic import BaseModel
@@ -142,9 +142,9 @@ class HarnessClient:
*,
on_notification: Callable[[Notification], None] | None = None,
notification_subscription: "NotificationSubscription | None" = None,
) -> None:
) -> str:
payload: JsonObject = {"sessionId": session_id, "contentBlocks": content_blocks}
self.request(
response = self.request(
"session/prompt",
payload,
response_model=_SessionPromptResponse,
@@ -152,6 +152,7 @@ class HarnessClient:
notification_filter=self._notification_belongs_to_session_tree(session_id),
notification_subscription=notification_subscription,
)
return response.messageId
def request(
self,
@@ -536,7 +537,7 @@ class NotificationSubscription:
class _SessionPromptResponse(BaseModel):
accepted: Literal[True]
messageId: str
class _ShutdownResponse(BaseModel):