style: apply ruff's automatic fixes and formatter
Mechanical only, and separated from the judgment calls that follow so the reviewable changes are not buried in a 98-file whitespace diff. 227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller modernisations. Then `ruff format` over src and tests: 98 files reformatted, 35 already conforming. No file among the unused-import findings defines __all__ or is an __init__.py, so nothing here removes a re-export. `make test`: 658 passed, unchanged from HEAD. Two things observed while verifying, neither addressed here: `pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an `e2e` marker that is not registered, and the config is strict about markers. This fails identically at HEAD, so it predates this change; `make test` passes because it ignores tests/e2e, tests/integration and tests/contracts. test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run with these changes and passed on the next, passes in isolation with them, and fails in isolation at HEAD. It is order- or timing-dependent, not a regression from this commit — established by running the full suite both ways rather than by reasoning about which change could have caused it. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+83
-83
@@ -30,8 +30,10 @@ logger = get_logger(__name__)
|
||||
# Stream Event Types
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class StreamEventType(str, Enum):
|
||||
"""Streaming event types for Responses API."""
|
||||
|
||||
REASONING_SUMMARY_DELTA = "response.reasoning_summary_text.delta"
|
||||
REASONING_SUMMARY_DONE = "response.reasoning_summary_text.done"
|
||||
OUTPUT_TEXT_DELTA = "response.output_text.delta"
|
||||
@@ -46,30 +48,38 @@ class StreamEventType(str, Enum):
|
||||
# Stream Event Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class ReasoningSummaryDelta(CustomBaseModel):
|
||||
"""Reasoning summary text delta event."""
|
||||
event: Literal[StreamEventType.REASONING_SUMMARY_DELTA] = StreamEventType.REASONING_SUMMARY_DELTA
|
||||
|
||||
event: Literal[StreamEventType.REASONING_SUMMARY_DELTA] = (
|
||||
StreamEventType.REASONING_SUMMARY_DELTA
|
||||
)
|
||||
delta: str
|
||||
|
||||
|
||||
class ReasoningSummaryDone(CustomBaseModel):
|
||||
"""Reasoning summary completion event."""
|
||||
|
||||
event: Literal[StreamEventType.REASONING_SUMMARY_DONE] = StreamEventType.REASONING_SUMMARY_DONE
|
||||
|
||||
|
||||
class OutputTextDelta(CustomBaseModel):
|
||||
"""Output text delta event."""
|
||||
|
||||
event: Literal[StreamEventType.OUTPUT_TEXT_DELTA] = StreamEventType.OUTPUT_TEXT_DELTA
|
||||
delta: str
|
||||
|
||||
|
||||
class OutputTextDone(CustomBaseModel):
|
||||
"""Output text completion event."""
|
||||
|
||||
event: Literal[StreamEventType.OUTPUT_TEXT_DONE] = StreamEventType.OUTPUT_TEXT_DONE
|
||||
|
||||
|
||||
class FunctionCallDelta(CustomBaseModel):
|
||||
"""Function call arguments delta event."""
|
||||
|
||||
event: Literal[StreamEventType.FUNCTION_CALL_DELTA] = StreamEventType.FUNCTION_CALL_DELTA
|
||||
delta: str
|
||||
name: str | None = None # Only in first chunk
|
||||
@@ -77,31 +87,34 @@ class FunctionCallDelta(CustomBaseModel):
|
||||
|
||||
class FunctionCallDone(CustomBaseModel):
|
||||
"""Function call completion event."""
|
||||
|
||||
event: Literal[StreamEventType.FUNCTION_CALL_DONE] = StreamEventType.FUNCTION_CALL_DONE
|
||||
|
||||
|
||||
class ResponseDone(CustomBaseModel):
|
||||
"""Response completion event with full response."""
|
||||
|
||||
event: Literal[StreamEventType.RESPONSE_DONE] = StreamEventType.RESPONSE_DONE
|
||||
response: Response
|
||||
|
||||
|
||||
class ErrorEvent(CustomBaseModel):
|
||||
"""Error event."""
|
||||
|
||||
event: Literal[StreamEventType.ERROR] = StreamEventType.ERROR
|
||||
error: dict
|
||||
|
||||
|
||||
# Union type for all stream events
|
||||
StreamEvent = (
|
||||
ReasoningSummaryDelta |
|
||||
ReasoningSummaryDone |
|
||||
OutputTextDelta |
|
||||
OutputTextDone |
|
||||
FunctionCallDelta |
|
||||
FunctionCallDone |
|
||||
ResponseDone |
|
||||
ErrorEvent
|
||||
ReasoningSummaryDelta
|
||||
| ReasoningSummaryDone
|
||||
| OutputTextDelta
|
||||
| OutputTextDone
|
||||
| FunctionCallDelta
|
||||
| FunctionCallDone
|
||||
| ResponseDone
|
||||
| ErrorEvent
|
||||
)
|
||||
|
||||
|
||||
@@ -109,6 +122,7 @@ StreamEvent = (
|
||||
# Streaming Coordinator
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class StreamingCoordinator:
|
||||
"""
|
||||
Coordinates streaming from agents to SSE format.
|
||||
@@ -123,7 +137,7 @@ class StreamingCoordinator:
|
||||
|
||||
async def stream_response_with_steward(
|
||||
self,
|
||||
request: "ResponseRequest" # type: ignore # Forward reference
|
||||
request: "ResponseRequest", # type: ignore # Forward reference
|
||||
) -> AsyncGenerator[StreamEvent, None]:
|
||||
"""
|
||||
Stream response with Steward preprocessing and two-phase Tatlock execution.
|
||||
@@ -181,10 +195,13 @@ class StreamingCoordinator:
|
||||
|
||||
# Check if direct delegation is recommended
|
||||
delegation_agents = {"biographer", "librarian", "housekeeper"}
|
||||
delegation_only = all(
|
||||
cap in delegation_agents
|
||||
for cap in enriched.recommendation.recommended_capabilities
|
||||
) and enriched.recommendation.recommended_capabilities
|
||||
delegation_only = (
|
||||
all(
|
||||
cap in delegation_agents
|
||||
for cap in enriched.recommendation.recommended_capabilities
|
||||
)
|
||||
and enriched.recommendation.recommended_capabilities
|
||||
)
|
||||
|
||||
tatlock = TatlockAgent()
|
||||
|
||||
@@ -222,7 +239,7 @@ class StreamingCoordinator:
|
||||
# Stream the synthesized response
|
||||
chunk_size = 50
|
||||
for i in range(0, len(tatlock_response), chunk_size):
|
||||
yield OutputTextDelta(delta=tatlock_response[i:i + chunk_size])
|
||||
yield OutputTextDelta(delta=tatlock_response[i : i + chunk_size])
|
||||
await asyncio.sleep(0.02)
|
||||
|
||||
yield OutputTextDone()
|
||||
@@ -231,12 +248,10 @@ class StreamingCoordinator:
|
||||
message_item = MessageOutputItem(
|
||||
id=f"msg_{generate_id()}",
|
||||
role="assistant",
|
||||
content=[OutputTextContent(
|
||||
type="output_text",
|
||||
text=tatlock_response,
|
||||
annotations=[]
|
||||
)],
|
||||
status="completed"
|
||||
content=[
|
||||
OutputTextContent(type="output_text", text=tatlock_response, annotations=[])
|
||||
],
|
||||
status="completed",
|
||||
)
|
||||
output_items.append(message_item)
|
||||
|
||||
@@ -252,7 +267,7 @@ class StreamingCoordinator:
|
||||
model=request.model,
|
||||
status="completed",
|
||||
output=output_items,
|
||||
usage=usage
|
||||
usage=usage,
|
||||
)
|
||||
|
||||
# Track conversation history
|
||||
@@ -319,17 +334,11 @@ class StreamingCoordinator:
|
||||
try:
|
||||
# Execute delegation
|
||||
if agent == "librarian":
|
||||
result = await delegate_to_librarian(
|
||||
task=user_message, context=context
|
||||
)
|
||||
result = await delegate_to_librarian(task=user_message, context=context)
|
||||
elif agent == "biographer":
|
||||
result = await delegate_to_biographer(
|
||||
task=user_message, context=context
|
||||
)
|
||||
result = await delegate_to_biographer(task=user_message, context=context)
|
||||
elif agent == "housekeeper":
|
||||
result = await delegate_to_housekeeper(
|
||||
task=user_message, context=context
|
||||
)
|
||||
result = await delegate_to_housekeeper(task=user_message, context=context)
|
||||
else:
|
||||
result = None
|
||||
|
||||
@@ -366,17 +375,19 @@ class StreamingCoordinator:
|
||||
yield ReasoningSummaryDone()
|
||||
|
||||
if results is not None:
|
||||
results.update({
|
||||
"tools_called": tools_called,
|
||||
"expert_results": expert_results,
|
||||
"tool_outputs": {},
|
||||
"raw_output": "",
|
||||
"think_messages": think_messages,
|
||||
})
|
||||
results.update(
|
||||
{
|
||||
"tools_called": tools_called,
|
||||
"expert_results": expert_results,
|
||||
"tool_outputs": {},
|
||||
"raw_output": "",
|
||||
"think_messages": think_messages,
|
||||
}
|
||||
)
|
||||
|
||||
async def stream_response(
|
||||
self,
|
||||
request: "ResponseRequest" # type: ignore # Forward reference
|
||||
request: "ResponseRequest", # type: ignore # Forward reference
|
||||
) -> AsyncGenerator[StreamEvent, None]:
|
||||
"""
|
||||
Coordinate streaming from agent to SSE events.
|
||||
@@ -435,18 +446,13 @@ class StreamingCoordinator:
|
||||
elif item.type == "function_call":
|
||||
# Stream function call arguments
|
||||
# First chunk includes name
|
||||
yield FunctionCallDelta(
|
||||
name=item.data["name"],
|
||||
delta=""
|
||||
)
|
||||
yield FunctionCallDelta(name=item.data["name"], delta="")
|
||||
|
||||
# Stream arguments in chunks
|
||||
args = item.data["arguments"]
|
||||
chunk_size = 20
|
||||
for i in range(0, len(args), chunk_size):
|
||||
yield FunctionCallDelta(
|
||||
delta=args[i:i+chunk_size]
|
||||
)
|
||||
yield FunctionCallDelta(delta=args[i : i + chunk_size])
|
||||
await asyncio.sleep(0.03)
|
||||
|
||||
yield FunctionCallDone()
|
||||
@@ -458,7 +464,7 @@ class StreamingCoordinator:
|
||||
# Only stream the NEW text (delta) since last update
|
||||
if current_text.startswith(last_message_text):
|
||||
# Extract only the new portion
|
||||
delta_text = current_text[len(last_message_text):]
|
||||
delta_text = current_text[len(last_message_text) :]
|
||||
|
||||
if delta_text:
|
||||
# Stream the delta text in chunks while preserving formatting
|
||||
@@ -466,17 +472,16 @@ class StreamingCoordinator:
|
||||
chunk_size = 50 # characters per chunk
|
||||
|
||||
for i in range(0, len(delta_text), chunk_size):
|
||||
chunk = delta_text[i:i+chunk_size]
|
||||
chunk = delta_text[i : i + chunk_size]
|
||||
|
||||
# Check stop sequences on full accumulated text
|
||||
stop_found, text_before_stop = self._check_stop_sequence(
|
||||
current_text,
|
||||
request.stop
|
||||
current_text, request.stop
|
||||
)
|
||||
|
||||
if stop_found:
|
||||
# Only emit remaining delta before stop
|
||||
remaining = text_before_stop[len(last_message_text):]
|
||||
remaining = text_before_stop[len(last_message_text) :]
|
||||
if remaining:
|
||||
yield OutputTextDelta(delta=remaining)
|
||||
yield OutputTextDone()
|
||||
@@ -508,11 +513,12 @@ class StreamingCoordinator:
|
||||
model=request.model,
|
||||
status="completed",
|
||||
output=self._convert_output_items(output_items),
|
||||
usage=usage
|
||||
usage=usage,
|
||||
)
|
||||
|
||||
# Track conversation history (import here to avoid circular dependency)
|
||||
from src.responses.service import _conversation_history
|
||||
|
||||
conversation_id = await _conversation_history.get_conversation_id(request)
|
||||
await _conversation_history.add_response(conversation_id, final_response)
|
||||
|
||||
@@ -534,24 +540,30 @@ class StreamingCoordinator:
|
||||
converted = []
|
||||
for item in items:
|
||||
if item.type == "message":
|
||||
converted.append(MessageOutputItem(
|
||||
id=item.id,
|
||||
content=[OutputTextContent(**c) for c in item.data["content"]],
|
||||
status=item.data.get("status", "completed")
|
||||
))
|
||||
converted.append(
|
||||
MessageOutputItem(
|
||||
id=item.id,
|
||||
content=[OutputTextContent(**c) for c in item.data["content"]],
|
||||
status=item.data.get("status", "completed"),
|
||||
)
|
||||
)
|
||||
elif item.type == "reasoning":
|
||||
converted.append(ReasoningOutputItem(
|
||||
id=item.id,
|
||||
summary=item.data["summary"],
|
||||
status=item.data.get("status", "completed")
|
||||
))
|
||||
converted.append(
|
||||
ReasoningOutputItem(
|
||||
id=item.id,
|
||||
summary=item.data["summary"],
|
||||
status=item.data.get("status", "completed"),
|
||||
)
|
||||
)
|
||||
elif item.type == "function_call":
|
||||
converted.append(FunctionCallOutputItem(
|
||||
id=item.id,
|
||||
name=item.data["name"],
|
||||
arguments=item.data["arguments"],
|
||||
status=item.data.get("status", "completed")
|
||||
))
|
||||
converted.append(
|
||||
FunctionCallOutputItem(
|
||||
id=item.id,
|
||||
name=item.data["name"],
|
||||
arguments=item.data["arguments"],
|
||||
status=item.data.get("status", "completed"),
|
||||
)
|
||||
)
|
||||
|
||||
return converted
|
||||
|
||||
@@ -576,18 +588,10 @@ class StreamingCoordinator:
|
||||
error_type = "internal_error"
|
||||
code = 500
|
||||
|
||||
return ErrorEvent(
|
||||
error={
|
||||
"type": error_type,
|
||||
"message": str(error),
|
||||
"code": code
|
||||
}
|
||||
)
|
||||
return ErrorEvent(error={"type": error_type, "message": str(error), "code": code})
|
||||
|
||||
def _check_stop_sequence(
|
||||
self,
|
||||
accumulated_text: str,
|
||||
stop_sequences: list[str] | None
|
||||
self, accumulated_text: str, stop_sequences: list[str] | None
|
||||
) -> tuple[bool, str]:
|
||||
"""
|
||||
Check if any stop sequence is encountered.
|
||||
@@ -624,11 +628,7 @@ class StreamingCoordinator:
|
||||
"""
|
||||
return len(text) // 4
|
||||
|
||||
def _check_max_tokens(
|
||||
self,
|
||||
current_tokens: int,
|
||||
max_tokens: int | None
|
||||
) -> bool:
|
||||
def _check_max_tokens(self, current_tokens: int, max_tokens: int | None) -> bool:
|
||||
"""
|
||||
Check if max tokens limit reached.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user