Skip to content

Commit b2b2063

Browse files
committed
feat: enhanced support for results directory and more metadata
Signed-off-by: vsoch <[email protected]>
1 parent b447d25 commit b2b2063

File tree

6 files changed

+72
-5
lines changed

6 files changed

+72
-5
lines changed

fractale/agent/base.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import json
21
import os
32
import sys
43

54
import google.generativeai as genai
65

6+
from fractale.agent.decorators import callback, save_logs
77
import fractale.agent.defaults as defaults
88
import fractale.agent.logger as logger
99
import fractale.utils as utils
@@ -23,20 +23,29 @@ class Agent:
2323

2424
# name and description should be on the class
2525

26-
def __init__(self, use_cache=False):
26+
def __init__(self, use_cache=False, results_dir=None, incremental=False):
2727

2828
# Max attempts defaults to unlimited
2929
# We start counting at 1 for the user to see.
3030
# Eat your heart out, Matlab.
3131
self.attempts = 1
3232
self.max_attempts = None
3333

34+
# For now, assume this is for the manager.
35+
# We can optionally save incremental result objects
36+
self.results_dir = results_dir or os.getcwd()
37+
self.save_incremental = incremental
38+
3439
# The user can save if desired - caching the context to skip steps that already run.
3540
self.setup_cache(use_cache)
3641

42+
# This supports saving custom logs and step (attempt) metadata
43+
self.metadata = {}
44+
3745
# Custom initialization functions
3846
self.init()
3947

48+
@callback(save_logs)
4049
def run(self, context):
4150
"""
4251
Run the agent - a wrapper around internal function _run that prepares it.

fractale/agent/decorators.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import time
2+
3+
def callback(callback_func):
4+
"""
5+
A decorator that executes a callback function after the decorated function.
6+
7+
We need this so that specific functions for the agent can return objects that save
8+
one or more metadata items (automatically).
9+
"""
10+
def decorator(func):
11+
def wrapper(self, *args, **kwargs):
12+
# This is the original function
13+
start = time.time()
14+
result = func(self, *args, **kwargs)
15+
# Get the result and pass to the callback!
16+
end = time.time()
17+
callback_func(self, result, end-start)
18+
return result
19+
return wrapper
20+
return decorator
21+
22+
23+
def save_logs(instance, context, elapsed_time):
24+
"""
25+
If defined (requested by the user) save the stage result.
26+
"""
27+
result = context.get('result')
28+
if not instance.save_incremental or not result:
29+
return
30+
if "logs" not in instance.metadata:
31+
instance.metadata['logs'] = []
32+
instance.metadata['logs'].append({"log": result, "elapsed_time_seconds": elapsed_time})

fractale/agent/kubernetes_job/agent.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ def get_diagnostics(self, job_name, namespace):
151151
Helper to collect rich error data for a failed job.
152152
"""
153153
print("[yellow]Gathering diagnostics for failed job...[/yellow]")
154+
print('TODO streamline')
155+
import IPython
156+
IPython.embed()
154157

155158
describe_job_cmd = ["kubectl", "describe", "job", job_name, "-n", namespace]
156159
job_description = subprocess.run(

fractale/agent/manager/agent.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,11 @@ def save_results(self, tracker):
7878
7979
Just ploop into pwd for now, we can eventually take a path.
8080
"""
81+
if not os.path.exists(self.results_dir):
82+
os.makedirs(self.results_dir)
8183
now = datetime.now()
8284
timestamp = now.strftime("%Y-%m-%d_%H-%M-%S")
83-
results_file = os.path.join(os.getcwd(), f"results-{timestamp}.json")
85+
results_file = os.path.join(self.results_dir, f"results-{timestamp}.json")
8486
utils.write_json(tracker, results_file)
8587

8688
def run(self, context):
@@ -167,7 +169,16 @@ def run_tasks(self, context, plan):
167169

168170
# Keep track of running the agent and the time it took
169171
# Also keep result of each build step (we assume there is one)
170-
tracker.append([step.agent, timer.elapsed_time, context.get("result")])
172+
# We will eventually want to also save the log.
173+
tracker.append(
174+
{
175+
"agent": step.agent,
176+
"elapsed_time_seconds": timer.elapsed_time,
177+
"result": context.get("result"),
178+
"attempts": step.attempts,
179+
"logs": step.logs,
180+
}
181+
)
171182

172183
# If we are successful, we go to the next step.
173184
# Not setting a return code indicates success.

fractale/cli/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ def get_parser():
9797
action="store_true",
9898
default=False,
9999
)
100+
agent.add_argument(
101+
"--results",
102+
help="Save to a custom results directory.",
103+
)
104+
agent.add_argument(
105+
"--incremental",
106+
help="Save incremental results for later inspection",
107+
action="store_true",
108+
default=False,
109+
)
100110

101111
# Add agent parsers
102112
parsers.register(agents)

fractale/cli/agent.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def main(args, extra, **kwargs):
1919

2020
# Get the agent and run!
2121
# Save determines if we want to save state to an output directory
22-
agent = agents[args.agent_name](use_cache=args.use_cache)
22+
agent = agents[args.agent_name](
23+
use_cache=args.use_cache, results_dir=args.results, incremental=args.incremental
24+
)
2325

2426
# This is the context - we can remove variables not needed
2527
context = vars(args)

0 commit comments

Comments
 (0)