Skip to content

Commit 09577c7

Browse files
authored
Merge pull request #119 from mpenn/mpenn_fix-llm-timeout
Fix LLM timeout handling and resolve symlinked skills in Docker builds
2 parents 9aeebcc + f2b9013 commit 09577c7

6 files changed

Lines changed: 66 additions & 19 deletions

File tree

cuopt-agent/cuopt_agent/configs/config-deepagent-eval.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ llms:
3838
api_key: ${NVIDIA_API_KEY}
3939
base_url: https://integrate.api.nvidia.com/v1/
4040
max_tokens: 16384
41+
do_auto_retry: false
4142

4243
functions:
4344
healthcheck:
@@ -57,17 +58,17 @@ workflow:
5758
- "AGENTS.md"
5859
workspace_dirs:
5960
- cuopt_agent/data/max_supply_what_ifs/input
60-
max_retries: 2
61+
max_retries: 16
6162
retry_backoff_factor: 2.0
6263
retry_initial_delay: 1.0
63-
retry_max_delay: 60.0
64+
retry_max_delay: 120.0
6465
system_prompt: |
65-
You are a supply-chain optimization coordinator powered by NVIDIA cuOpt.
66-
For optimization problems, delegate to the cuopt_sub_agent using the task() tool.
67-
For general questions, respond directly.
66+
You are a supply-chain optimization assistant powered by NVIDIA cuOpt.
67+
Solve the following optimization problem using Python and the cuOpt solver.
68+
Only write files in your working directory. Do not ask follow-up questions.
69+
Reference your skills to understand latest API usage before getting started.
70+
Answer simple questions directly.
6871
Report the optimal objective value as a single float in \boxed{...} on the last line.
69-
subagents:
70-
- cuopt_sub_agent
7172
7273
eval:
7374
general:

cuopt-agent/cuopt_agent/configs/config-deepagent.yml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ llms:
4545
api_key: ${NVIDIA_API_KEY}
4646
base_url: https://integrate.api.nvidia.com/v1/
4747
max_tokens: 16384
48+
do_auto_retry: false
4849

4950
functions:
5051
healthcheck:
@@ -63,17 +64,13 @@ workflow:
6364
- /skills/cuopt
6465
memory:
6566
- /AGENTS.md
66-
max_retries: 2
67+
max_retries: 16
6768
retry_backoff_factor: 2.0
6869
retry_initial_delay: 1.0
69-
retry_max_delay: 60.0
70+
retry_max_delay: 120.0
7071
system_prompt: |
71-
You are a supply-chain optimization coordinator powered by NVIDIA cuOpt.
72-
For optimization problems, delegate to the cuopt_sub_agent using the task() tool.
73-
For general questions, respond directly.
74-
# system_prompt: |
75-
# You are a supply-chain optimization assistant powered by NVIDIA cuOpt.
76-
# Solve the following optimization problem using Python and the cuOpt solver.
77-
# Only write files in your working directory. Do not ask follow-up questions.
78-
# Reference your skills to understand latest API usage before getting started.
79-
# Answer simple questions directly.
72+
You are a supply-chain optimization assistant powered by NVIDIA cuOpt.
73+
Solve the following optimization problem using Python and the cuOpt solver.
74+
Only write files in your working directory. Do not ask follow-up questions.
75+
Reference your skills to understand latest API usage before getting started.
76+
Answer simple questions directly.

cuopt-agent/cuopt_agent/docker/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ ENV PATH="/app/cuopt_agent/.venv/bin:$PATH"
3131
COPY cuopt_agent/configs /app/cuopt_agent/configs
3232
COPY cuopt_agent/data /app/cuopt_agent/data
3333
COPY skills /app/skills
34+
<<<<<<< HEAD
35+
COPY external/cuopt/skills /app/skills/cuopt
36+
=======
3437
COPY external/cuopt/skills /app/external/cuopt/skills
38+
>>>>>>> upstream/main
3539
COPY AGENTS.md /app/AGENTS.md
3640

3741
# CWD must be cuopt_agent/ so config relative paths resolve correctly:

cuopt-agent/cuopt_agent/docker/Dockerfile.dev

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ WORKDIR /app
2121
# Copy package and repo assets (build context: repo root)
2222
COPY cuopt_agent /app/cuopt_agent
2323
COPY skills /app/skills
24+
COPY external/cuopt/skills /app/skills/cuopt
2425
COPY AGENTS.md /app/AGENTS.md
2526

2627
# Install manually inside container (e.g. uv pip install --extra-index-url https://pypi.nvidia.com -e /app/cuopt_agent)

cuopt-agent/cuopt_agent/src/nat_cuopt_agent/function/deepagent_fn.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class DeepAgentConfig(FunctionBaseConfig, name="deepagent_fn"):
117117
description="Initial delay in seconds before first retry.",
118118
)
119119
retry_max_delay: float = Field(
120-
default=60.0,
120+
default=120.0,
121121
description="Maximum delay cap in seconds between retries.",
122122
)
123123
strip_reasoning_pattern: str = Field(
@@ -142,6 +142,7 @@ async def deep_agent(config: DeepAgentConfig, builder: Builder):
142142
SANDBOX_AGENTS_MD,
143143
SANDBOX_SKILLS_DIR,
144144
FixToolNamesMiddleware,
145+
ToolRetryMiddleware,
145146
kill_orphaned_children,
146147
populate_sandbox,
147148
resolve_skills_dirs,
@@ -224,6 +225,7 @@ async def _inner(chat_request_or_message: ChatRequestOrMessage) -> ChatResponse
224225
# Create a middleware chain for the agent to improve reliability and performance
225226
middleware = [
226227
FixToolNamesMiddleware(),
228+
ToolRetryMiddleware(),
227229
ModelRetryMiddleware(
228230
max_retries=config.max_retries,
229231
backoff_factor=config.retry_backoff_factor,

cuopt-agent/cuopt_agent/src/nat_cuopt_agent/function/utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import asyncio
1617
import logging
1718
import re
1819
import shutil
@@ -69,6 +70,47 @@ async def awrap_model_call(self, request, handler):
6970
return self._patch(await handler(request))
7071

7172

73+
class ToolRetryMiddleware(AgentMiddleware):
74+
"""Retries failed tool calls with exponential backoff.
75+
76+
Provides uniform retry coverage for all tools. Some tools (e.g., Tavily)
77+
have their own internal retry; this middleware wraps the outer call so
78+
tools without retry (knowledge layer, paper search) are also covered.
79+
"""
80+
81+
def __init__(
82+
self,
83+
max_retries: int = 16,
84+
backoff_factor: float = 2.0,
85+
initial_delay: float = 1.0,
86+
):
87+
self.max_retries = max_retries
88+
self.backoff_factor = backoff_factor
89+
self.initial_delay = initial_delay
90+
91+
async def awrap_tool_call(self, request, handler):
92+
"""Retry tool calls on failure with exponential backoff."""
93+
delay = self.initial_delay
94+
last_exception = None
95+
for attempt in range(self.max_retries + 1):
96+
try:
97+
return await handler(request)
98+
except Exception as e:
99+
last_exception = e
100+
if attempt < self.max_retries:
101+
tool_name = request.tool_call.get("name", "?") if hasattr(request, "tool_call") else "?"
102+
logger.warning(
103+
"Tool %s failed (attempt %d/%d): %s",
104+
tool_name,
105+
attempt + 1,
106+
self.max_retries + 1,
107+
e,
108+
)
109+
await asyncio.sleep(delay)
110+
delay *= self.backoff_factor
111+
raise last_exception
112+
113+
72114
def strip_pattern(text: str, pattern: re.Pattern[str] | None) -> str:
73115
"""Remove all regex matches from *text*.
74116

0 commit comments

Comments
 (0)