Skip to content
Merged
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
2 changes: 2 additions & 0 deletions pycodeloop/core/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ def run(
or `cancel_event` is set — checked at each turn boundary and
before every tool call, never mid-request, so an in-flight
provider call always finishes first."""
self._provider_index = 0
self.provider = self._provider_chain[0]
session = session or Session(system_prompt=self.system_prompt)
session.add_user(prompt, images=images)
self._notify_message()
Expand Down
28 changes: 28 additions & 0 deletions tests/core/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,34 @@ def test_raises_when_every_provider_in_the_chain_fails(self):
self.assertEqual(primary.calls, 4)
self.assertEqual(secondary.calls, 4)

def test_a_new_run_call_retries_the_primary_even_after_a_prior_fallback(
self,
):
"""Issue #18: falling back within one run() must not be permanent
across the agent's lifetime — a later run() call (a new user
message) should give the primary another chance instead of
staying stuck on the fallback forever."""
primary = FlakyProvider(fail_times=4)
secondary = FlakyProvider(fail_times=0)
agent = Agent(
provider=primary,
fallback_providers=[secondary],
tools=[],
)

first = agent.run("hi")
self.assertEqual(first, "ok")
self.assertIs(agent.provider, secondary)
self.assertEqual(primary.calls, 4)
self.assertEqual(secondary.calls, 1)

second = agent.run("hi again")

self.assertEqual(second, "ok")
self.assertIs(agent.provider, primary)
self.assertEqual(primary.calls, 5)
self.assertEqual(secondary.calls, 1)

def test_second_turn_after_fallback_does_not_retry_the_dead_primary(self):
"""Regression: rebuilding `[self.provider, *self.fallback_providers]`
fresh on every `_complete` call put the now-active fallback in the
Expand Down
Loading