Skip to content

Commit 3f87381

Browse files
Merge pull request #141 from conductor-oss/fix/agent-e2e-llm-budget
test: skip agent e2e when LLM budget is exhausted
2 parents 9e335df + a0d20d9 commit 3f87381

3 files changed

Lines changed: 73 additions & 0 deletions

File tree

‎e2e/src/test/java/BaseTest.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Map;
2020

2121
import org.junit.jupiter.api.BeforeAll;
22+
import org.junit.jupiter.api.extension.ExtendWith;
2223

2324
import io.orkes.conductor.client.model.agent.CompileResponse;
2425

@@ -37,6 +38,7 @@
3738
* <li>Helper to extract agentDef from a plan() result</li>
3839
* </ul>
3940
*/
41+
@ExtendWith(LlmBudgetExhaustionExtension.class)
4042
public abstract class BaseTest {
4143

4244
/** API URL for the Conductor server (includes /api suffix). */
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import java.util.Locale;
2+
3+
import org.junit.jupiter.api.extension.ExtensionContext;
4+
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
5+
import org.opentest4j.TestAbortedException;
6+
7+
/** Skips live LLM tests when a provider's configured API usage budget is exhausted. */
8+
final class LlmBudgetExhaustionExtension implements TestExecutionExceptionHandler {
9+
10+
static final String USAGE_LIMIT_MESSAGE = "reached your specified api usage limits";
11+
12+
@Override
13+
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
14+
if (isUsageLimitError(throwable)) {
15+
throw new TestAbortedException("LLM API usage budget is exhausted", throwable);
16+
}
17+
throw throwable;
18+
}
19+
20+
static boolean isUsageLimitError(Throwable throwable) {
21+
for (Throwable current = throwable; current != null; current = current.getCause()) {
22+
String message = current.getMessage();
23+
if (message != null && message.toLowerCase(Locale.ROOT).contains(USAGE_LIMIT_MESSAGE)) {
24+
return true;
25+
}
26+
}
27+
return false;
28+
}
29+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import org.junit.jupiter.api.Test;
2+
import org.opentest4j.TestAbortedException;
3+
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertSame;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
class LlmBudgetExhaustionExtensionTest {
10+
11+
@Test
12+
void detectsUsageLimitMessageInCauseChain() {
13+
Throwable failure = new AssertionError(
14+
"agent failed",
15+
new IllegalStateException("You have reached your specified API usage limits. Try again later."));
16+
17+
assertTrue(LlmBudgetExhaustionExtension.isUsageLimitError(failure));
18+
}
19+
20+
@Test
21+
void doesNotMaskOtherLlmFailures() {
22+
Throwable failure = new AssertionError("Agent failed: invalid model");
23+
24+
assertFalse(LlmBudgetExhaustionExtension.isUsageLimitError(failure));
25+
}
26+
27+
@Test
28+
void abortsUsageLimitFailuresAndRethrowsOtherFailures() {
29+
LlmBudgetExhaustionExtension extension = new LlmBudgetExhaustionExtension();
30+
Throwable budgetFailure =
31+
new AssertionError("You have reached your specified API usage limits.");
32+
Throwable otherFailure = new AssertionError("Agent failed: invalid model");
33+
34+
assertThrows(
35+
TestAbortedException.class,
36+
() -> extension.handleTestExecutionException(null, budgetFailure));
37+
Throwable rethrown = assertThrows(
38+
Throwable.class,
39+
() -> extension.handleTestExecutionException(null, otherFailure));
40+
assertSame(otherFailure, rethrown);
41+
}
42+
}

0 commit comments

Comments
 (0)