Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion gently/app/orchestration/timelapse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,6 @@ def _check_interval_rules(
embryo_id=embryo_id,
detector_name=detector_name,
stage=stage,
timepoint=estate.timepoints_acquired,
):
# Round-based: interval rules now modify the global interval
old_interval = self._base_interval_seconds
Expand Down
43 changes: 39 additions & 4 deletions gently/harness/perception/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,8 @@ async def _run_reasoning_loop(
"""
Run the interleaved reasoning loop with tool use.

Returns (PerceptionResult, ReasoningTrace)
Returns (PerceptionResult, ReasoningTrace, messages) where messages is the
full conversation history, so callers can continue the conversation.
"""
trace = ReasoningTrace()

Expand Down Expand Up @@ -591,8 +592,9 @@ async def _run_reasoning_loop(
))

# Parse and return
messages.append({"role": "assistant", "content": [{"type": "text", "text": text_response}]})
result = self._parse_response(text_response)
return result, trace
return result, trace, messages

# Handle tool use
if response.stop_reason == "tool_use":
Expand Down Expand Up @@ -620,12 +622,33 @@ async def _run_reasoning_loop(
tool_input=block.input,
))

assistant_content = []
for block in response.content:
if block.type == "text":
assistant_content.append({"type": "text", "text": block.text})
elif block.type == "tool_use":
assistant_content.append({
"type": "tool_use",
"id": block.id,
"name": block.name,
"input": block.input,
})
messages.append({"role": "assistant", "content": assistant_content})

# Run verification and return result
result = await self._handle_verification_request(
verification_block.input,
trace,
)
return result, trace
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": verification_block.id,
"content": f"Verification complete: stage={result.stage}, confidence={result.confidence:.0%}",
}],
})
return result, trace, messages

# Build assistant message with the response
assistant_content = []
Expand Down Expand Up @@ -688,7 +711,7 @@ async def _run_reasoning_loop(

# Max iterations reached - parse last response
logger.warning(f"Max reasoning iterations ({max_iterations}) reached")
return self._parse_response(""), trace
return self._parse_response(""), trace, messages

def _handle_tool_call(
self,
Expand Down Expand Up @@ -1060,6 +1083,18 @@ def _build_cached_system_prompt(self) -> List[Dict]:
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}]

def _build_reconsider_prompt(self, result: "PerceptionResult", turn: int) -> str:
raise NotImplementedError(
"Multishot reconsideration is not yet implemented. "
"Set multishot_turns=0 (the default) to disable."
)

async def _call_claude(self, messages: List[Dict], include_tools: bool = True) -> Any:
raise NotImplementedError(
"Multishot reconsideration is not yet implemented. "
"Set multishot_turns=0 (the default) to disable."
)

async def _call_claude_with_tools(self, messages: List[Dict]) -> Any:
"""Call Claude API with tools enabled for interleaved reasoning."""
try:
Expand Down
4 changes: 4 additions & 0 deletions gently/harness/perception/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,7 @@ class PerceptionResult:
candidate_stages: Optional[List[CandidateStage]] = None # Candidates from Phase 1
multi_phase_trace: Optional[MultiPhaseReasoningTrace] = None # Full multi-phase trace
phase_count: int = 1 # Number of phases executed (1, 2, or 3)

# Multishot reconsideration: the first answer before any reconsider turns
initial_stage: Optional[str] = None
initial_confidence: Optional[float] = None