refactor: remove per-followup result attribution
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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}]
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user