-
Notifications
You must be signed in to change notification settings - Fork 786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Record logs into step artifacts #1339
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks good to me! Reviewed everything up to b2cb9c4 in 2 minutes and 53 seconds
More details
- Looked at
196
lines of code in8
files - Skipped
0
files when reviewing. - Skipped posting
1
drafted comments based on config settings.
1. skyvern/forge/agent.py:1778
- Draft comment:
The try-except block for recording the Skyvern log is redundant here as it is already handled in theupdate_step
method. Consider removing this block to avoid code duplication. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable:
The comment claims redundancy, but the diff does not show any evidence that theupdate_step
method handles the same logging functionality. Without strong evidence of redundancy, the comment should be removed. The comment does not provide actionable or clear evidence of the issue.
I might be missing the full implementation details of theupdate_step
method, which could potentially handle the logging. However, the diff does not provide this information, so I should not assume it.
The diff should provide strong evidence of redundancy for the comment to be valid. Without this evidence, the comment should be removed.
Remove the comment as it lacks strong evidence of redundancy in the try-except block for logging.
Workflow ID: wflow_tO2RAXSK8tQ1bpap
You can customize Ellipsis with 👍 / 👎 feedback, review rules, user-specific overrides, quiet
mode, and more.
I don't think JSON logs are useful to the reader. Can we generate 2 artifacts instead?
|
skyvern/forge/agent.py
Outdated
log = skyvern_context.current().log | ||
current_step_log = [entry for entry in log if entry.get("step_id", "") == step.step_id] | ||
log_json = json.dumps(current_step_log, cls=SkyvernLogEncoder, indent=2) | ||
await app.ARTIFACT_MANAGER.create_artifact( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way we can also capture task and workflow level logs? Even if we don't render them in the UI today
We historically associate logs like that w/ the first step, although @wintonzheng is currently overhauling it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah we just added new columns to the artifacts db: https://github.com/Skyvern-AI/skyvern/blob/main/skyvern/forge/sdk/db/models.py#L169-L174
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/Skyvern-AI/skyvern/pull/1345/files
here's the example of supporting artifact upload for observer thought.
going forward we can add support for task level and workflow run level artifacts
skyvern/forge/agent.py
Outdated
|
||
try: | ||
log = skyvern_context.current().log | ||
current_step_log = [entry for entry in log if entry.get("step_id", "") == step.step_id] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this PR is a good start.
There are a couple of problems i'm seeing right now (not because of the code here) that we need keep iterating:
- sometimes we don't have step_id in a log. example. TODO: We need to add step_id to all the logs
- for each task in a workflow run, we don't set the task_id in the SkyvernContext before the task starts. this means many logs won't have task_id - if we want to store logs according to the "task_id" field in the json. TODO: set task_id in SkyvernContext before a task starts and reset it after a task ends (completed, failed, terminated, canceled)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: We need to add step_id to all the logs
@wintonzheng are we certain that each log must have a step_id?
From what I see in the logs when I ran tasks there are several logs in the beginning that don't belong to any step:
2024-12-09T11:09:09.220558Z [info ] Created new task organization_id=o_333794107196816612 proxy_location=RESIDENTIAL task_id=tsk_335262355392558810 url=https://www.nytimes.com/books/best-sellers
2024-12-09T11:09:09.220672Z [info ] Executing task using background task executor task_id=tsk_335262355392558810
2024-12-09T11:09:09.266772Z [info ] Creating browser state for task task_id=tsk_335262355392558810
2024-12-09T11:09:12.092991Z [info ] browser console log is saved log_path=./log/2024-12-09/95733df0-f2d8-428d-89ce-f71221f0cbe1.log
2024-12-09T11:09:12.326437Z [info ] Trying to navigate to https://www.nytimes.com/books/best-sellers and waiting for 5 seconds. retry_time=0 url=https://www.nytimes.com/books/best-sellers
2024-12-09T11:09:14.148604Z [info ] Page loading time loading_time=1.8220469951629639 url=https://www.nytimes.com/books/best-sellers
2024-12-09T11:09:19.148456Z [info ] Successfully went to https://www.nytimes.com/books/best-sellers retry_time=0 url=https://www.nytimes.com/books/best-sellers
2024-12-09T11:09:19.162015Z [info ] Starting agent step step_id=stp_335262355392558812 step_order=0 step_retry=0 task_id=tsk_335262355392558810
Here first 7 logs would be without a step because the first step is not created yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wintonzheng do I undestand correctly that we want to create task and step ids in advance?
This way the logs that are currently not associated with a step or task because step/task not created yet would be associated with the first task and first step?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wintonzheng are we certain that each log must have a step_id?
no, step_id needs to be manually defined in LOG.info("xxx", step_id="stp_xxxx")
nowadays. those logs you shared here don't belong to any step so it's okay many logs don't have step_id
.
@wintonzheng do I undestand correctly that we want to create task and step ids in advance? This way the logs that are currently not associated with a step or task because step/task not created yet would be associated with the first task and first step?
yep, exactly what i mean. we need to backfill step_id and task_id in some logs.
@suchintan here are logs in text format, I'm now generating both text and json artifacts I'm only showing the text logs in the UI for now. |
This is perfect |
@@ -10,6 +10,8 @@ | |||
ArtifactType.SCREENSHOT_LLM: "png", | |||
ArtifactType.SCREENSHOT_ACTION: "png", | |||
ArtifactType.SCREENSHOT_FINAL: "png", | |||
ArtifactType.SKYVERN_LOG: "log", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Step log..
Task log..
Workflow log...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't think adding STEP_LOG, TASK_LOG, WORKFLOW_LOG helps.
It's better to use step_id, task_id and workflow_run_id columns to filter artifacts. Also, step log and task log are not mutually exclusive. TaskLogs should be the super set of StepLogs. WorkflowRunLogs should be the super set of TaskLogs. We don't have to create multiple sets of log artifacts that have duplicated data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I agree with this -- why not have multiple artifact types that may or may not include duplicate data? It makes the implementation + writing of data + reading of data dead simple.
Why is it better to use step_id / task_id / workflow_run_id columns to filter artifacts w/ the same name + type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly in this case we'll store workflow and task level logs in the step artifact folders
Current implementation of bulid
uri always assumes step:
def build_uri(self, artifact_id: str, step: Step, artifact_type: ArtifactType) -> str:
file_ext = FILE_EXTENTSION_MAP[artifact_type]
return f"file://{self.artifact_path}/{step.task_id}/{step.order:02d}_{step.retry_index}_{step.step_id}/{datetime.utcnow().isoformat()}_{artifact_id}_{artifact_type}.{file_ext}"
We could define a build uri method for logs that would take entity type and id
So that the uri would look like this:
return f"file://{self.artifact_path}/{entity_type}_{entity_id}/{datetime.utcnow().isoformat()}_{artifact_type}.{file_ext}"
This way we would be able to fetch logs on the frontend using a similar query as for the other artifacts
We would be able to use artifact type for all the levels
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@suchintan no concern of data duplication. you're wright, having more artifact_types makes the implementation easier.
@@ -65,11 +63,11 @@ function ScrollableActionList({ | |||
onClick={() => onActiveIndexChange(i)} | |||
onMouseEnter={() => { | |||
queryClient.prefetchQuery({ | |||
queryKey: ["task", taskId, "steps", action.stepId, "artifacts"], | |||
queryKey: ["step", action.stepId, "artifacts"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@msalihaltun can you review?
LOG = structlog.get_logger() | ||
|
||
def primary_key_from_log_entity_type(log_entity_type: LogEntityType) -> str: | ||
if log_entity_type == LogEntityType.STEP: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a couple of small comments. Code looks great.
from skyvern.forge.sdk.db.id import generate_artifact_id | ||
from skyvern.forge.sdk.models import Step | ||
from skyvern.forge.sdk.schemas.observers import ObserverCruise, ObserverThought | ||
|
||
LOG = structlog.get_logger(__name__) | ||
|
||
PRIMARY_KEY = Literal[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LawyZheng we're introducing new primary keys for artifacts
Now i think we need to ensure those aio tasks for new primary keys (workflow_run_id, step_id) will be awaited at cleanup time.
typing fix here: 63829fa feel free to pick that commit and we can merge this PR |
Recording step execution logs into artifacts.
During step, task and workflow_run execution we add log entries into the
log
field of thecontext
.Once step, task or workflow_run state is updated we record the entries into an artifact.
If there already is a log artifact for the given entity - it will be updated instead of creating a new one.
Assumptions
{entity_type}_update
methodLog Artifacts API Access
I've introduced a new endpoint to access artifacts for different entities:
Storing Log Artifact Files
Testing plan
Screenshots
Successful run:
Failing run:
Updates
Added text log formatter:
Important
This PR adds functionality to record step execution logs as
SkyvernLog
artifacts in JSON format, with updates to logging, context, and artifact management.log
field ofSkyvernContext
and stored asSkyvernLog
artifacts.SkyvernLogEncoder
.SkyvernLog
toArtifactType
inmodels.py
andtypes.ts
.FILE_EXTENSION_MAP
instorage/base.py
to includeSkyvernLog
withjson
extension.skyvern_logs_processor
inforge_log.py
to append logs toSkyvernContext
.skyvern_logs_processor
intosetup_logger()
.StepArtifacts.tsx
to displaySkyvernLog
artifacts in the UI.SkyvernLogEncoder
inskyvern_log_encoder.py
for custom JSON encoding of logs.This description was created by for b2cb9c4. It will automatically update as commits are pushed.