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
52 changes: 52 additions & 0 deletions packages/traceloop-sdk/tests/test_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,55 @@ def dataclass_workflow(data: TestDataClass):
assert task_span.parent.span_id == workflow_span.context.span_id
assert workflow_span.attributes[SpanAttributes.TRACELOOP_ENTITY_NAME] == "dataclass_workflow"
assert task_span.attributes[SpanAttributes.TRACELOOP_ENTITY_NAME] == "dataclass_task"


class FakeAsyncRequest:
async def json(self):
return {"ok": True}


class FakeSyncRequest:
def json(self):
return {"ok": True}


@pytest.mark.asyncio
async def test_async_workflow_with_async_json_method_argument(exporter, recwarn):
@workflow(name="request_workflow")
async def request_workflow(request: FakeAsyncRequest):
return {"ok": True}

await request_workflow(FakeAsyncRequest())

assert not any(
w.category is RuntimeWarning and "was never awaited" in str(w.message)
for w in recwarn
)

spans = exporter.get_finished_spans()
assert [span.name for span in spans] == ["request_workflow.workflow"]

workflow_span = spans[0]
assert json.loads(workflow_span.attributes[SpanAttributes.TRACELOOP_ENTITY_INPUT]) == {
"args": ["FakeAsyncRequest"],
"kwargs": {},
}
assert json.loads(workflow_span.attributes[SpanAttributes.TRACELOOP_ENTITY_OUTPUT]) == {"ok": True}


def test_workflow_with_sync_json_method_argument(exporter):
@workflow(name="request_workflow")
def request_workflow(request: FakeSyncRequest):
return {"ok": True}

request_workflow(FakeSyncRequest())

spans = exporter.get_finished_spans()
assert [span.name for span in spans] == ["request_workflow.workflow"]

workflow_span = spans[0]
assert json.loads(workflow_span.attributes[SpanAttributes.TRACELOOP_ENTITY_INPUT]) == {
"args": [{"ok": True}],
"kwargs": {},
}
assert json.loads(workflow_span.attributes[SpanAttributes.TRACELOOP_ENTITY_OUTPUT]) == {"ok": True}
8 changes: 7 additions & 1 deletion packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import dataclasses
import inspect
import json


Expand All @@ -15,7 +16,12 @@ def default(self, o):
return o.to_json()

if hasattr(o, "json"):
return o.json()
json_method = o.json
if callable(json_method) and not inspect.iscoroutinefunction(json_method):
result = json_method()
if not inspect.iscoroutine(result):
return result
Comment thread
max-deygin-traceloop marked this conversation as resolved.
Comment on lines +22 to +23

@OzBenSimhonTraceloop OzBenSimhonTraceloop Apr 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not inspect.iscoroutine(result):
return result
if not inspect.iscoroutine(result):
return result
else:
result.close()

Also lets verify in tests that no runtime warning is emitted

result.close()

if hasattr(o, "__class__"):
return o.__class__.__name__
Expand Down
Loading