Skip to content

Commit e93966f

Browse files
committed
support set span finish time
1 parent d53626f commit e93966f

6 files changed

Lines changed: 61 additions & 15 deletions

File tree

CHANGLOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
### Added
33
- add to_runnable decorator
44
- lcc tool message add name and tool_call_id
5+
- support set finish time of span
56

67
## [0.1.24] - 2026-01-16
78
### Added

cozeloop/decorator/decorator.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,6 @@ async def async_stream_wrapper(*args: Any, **kwargs: Any):
313313
else:
314314
return decorator(func)
315315

316-
317316
def to_runnable(
318317
self,
319318
func: Callable = None,
@@ -341,11 +340,15 @@ def sync_wrapper(*args: Any, **kwargs: Any):
341340
config = _convert_config(config)
342341
res = None
343342
try:
343+
extra = {}
344+
if len(args) > 0 and is_class_func(func):
345+
extra = {"_inner_class_self": args[0]}
346+
args = args[1:]
344347
inp = {
345348
"args": args,
346349
"kwargs": kwargs
347350
}
348-
res = RunnableLambda(_param_wrapped_func).invoke(input=inp, config=config)
351+
res = RunnableLambda(_param_wrapped_func).invoke(input=inp, config=config, **extra)
349352
if hasattr(res, "__iter__"):
350353
return res
351354
except StopIteration:
@@ -362,11 +365,15 @@ async def async_wrapper(*args: Any, **kwargs: Any):
362365
config = _convert_config(config)
363366
res = None
364367
try:
368+
extra = {}
369+
if len(args) > 0 and is_class_func(func):
370+
extra = {"_inner_class_self": args[0]}
371+
args = args[1:]
365372
inp = {
366373
"args": args,
367374
"kwargs": kwargs
368375
}
369-
res = await RunnableLambda(_param_wrapped_func_async).ainvoke(input=inp, config=config)
376+
res = await RunnableLambda(_param_wrapped_func_async).ainvoke(input=inp, config=config, **extra)
370377
if hasattr(res, "__aiter__"):
371378
return res
372379
except StopIteration:
@@ -387,11 +394,15 @@ def gen_wrapper(*args: Any, **kwargs: Any):
387394
config = kwargs.pop("config", None)
388395
config = _convert_config(config)
389396
try:
397+
extra = {}
398+
if len(args) > 0 and is_class_func(func):
399+
extra = {"_inner_class_self": args[0]}
400+
args = args[1:]
390401
inp = {
391402
"args": args,
392403
"kwargs": kwargs
393404
}
394-
gen = RunnableLambda(_param_wrapped_func).invoke(input=inp, config=config)
405+
gen = RunnableLambda(_param_wrapped_func).invoke(input=inp, config=config, *extra)
395406
try:
396407
for item in gen:
397408
yield item
@@ -405,11 +416,15 @@ async def async_gen_wrapper(*args: Any, **kwargs: Any):
405416
config = kwargs.pop("config", None)
406417
config = _convert_config(config)
407418
try:
419+
extra = {}
420+
if len(args) > 0 and is_class_func(func):
421+
extra = {"_inner_class_self": args[0]}
422+
args = args[1:]
408423
inp = {
409424
"args": args,
410425
"kwargs": kwargs
411426
}
412-
gen = RunnableLambda(_param_wrapped_func_async).invoke(input=inp, config=config)
427+
gen = RunnableLambda(_param_wrapped_func_async).invoke(input=inp, config=config, **extra)
413428
items = []
414429
try:
415430
async for item in gen:
@@ -428,15 +443,25 @@ async def async_gen_wrapper(*args: Any, **kwargs: Any):
428443
raise e
429444

430445
# for convert parameter
431-
def _param_wrapped_func(input_dict: dict) -> Any:
432-
args = input_dict.get("args", ())
433-
kwargs = input_dict.get("kwargs", {})
434-
return func(*args, **kwargs)
435-
436-
async def _param_wrapped_func_async(input_dict: dict) -> Any:
437-
args = input_dict.get("args", ())
438-
kwargs = input_dict.get("kwargs", {})
439-
return await func(*args, **kwargs)
446+
def _param_wrapped_func(input_dict: dict, **kwargs) -> Any:
447+
real_args = input_dict.get("args", ())
448+
real_kwargs = input_dict.get("kwargs", {})
449+
450+
inner_class_self = kwargs.get("_inner_class_self", None)
451+
if inner_class_self is not None:
452+
real_args = (inner_class_self, *real_args)
453+
454+
return func(*real_args, **real_kwargs)
455+
456+
async def _param_wrapped_func_async(input_dict: dict, **kwargs) -> Any:
457+
real_args = input_dict.get("args", ())
458+
real_kwargs = input_dict.get("kwargs", {})
459+
460+
inner_class_self = kwargs.get("_inner_class_self", None)
461+
if inner_class_self is not None:
462+
real_args = (inner_class_self, *real_args)
463+
464+
return await func(*real_args, **real_kwargs)
440465

441466
def _convert_config(config: RunnableConfig = None) -> RunnableConfig | None:
442467
if config is None:

cozeloop/internal/trace/noop_span.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ def set_system_tags(self, system_tags: Dict[str, Any]) -> None:
125125
def set_deployment_env(self, deployment_env: str) -> None:
126126
pass
127127

128+
def set_finish_time(self, finish_time: datetime) -> None:
129+
pass
130+
128131
def __enter__(self):
129132
return self
130133

cozeloop/internal/trace/span.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def __init__(self, span_type: str = '', name: str = '', space_id: str = '', trac
7878
self.space_id = space_id
7979
self.parent_span_id = parent_span_id
8080
self.start_time = start_time if start_time else datetime.now()
81+
self.finish_time: datetime = None
8182
self.duration = duration
8283
self.tag_map = tag_map if tag_map else {}
8384
self.system_tag_map = system_tag_map if system_tag_map else {}
@@ -396,6 +397,10 @@ def set_system_tags(self, system_tags: Dict[str, Any]) -> None:
396397
def set_deployment_env(self, deployment_env: str) -> None:
397398
self.set_tags({DEPLOYMENT_ENV: deployment_env})
398399

400+
def set_finish_time(self, finish_time: datetime) -> None:
401+
self.finish_time = finish_time
402+
403+
399404
def get_rectified_map(self, input_map: Dict[str, Any]) -> (Dict[str, Any], List[str], int):
400405
validate_map = {}
401406
cut_off_keys = []
@@ -541,7 +546,10 @@ def set_stat_info(self):
541546
if input_tokens > 0 or output_tokens > 0:
542547
self.set_tags({TOKENS: int(input_tokens) + int(output_tokens)})
543548

544-
duration = int((datetime.now().timestamp() - self.start_time.timestamp()) * 1000000)
549+
finish_time_stamp = datetime.now().timestamp()
550+
if self.finish_time is not None:
551+
finish_time_stamp = self.finish_time.timestamp()
552+
duration = int((finish_time_stamp - self.start_time.timestamp()) * 1000000)
545553
with self.lock:
546554
self.duration = duration
547555

cozeloop/span.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,18 @@ def set_system_tags(self, system_tags: Dict[str, Any]) -> None:
186186
Set system tags. DO NOT use this method unless you know what you are doing.
187187
"""
188188

189+
@abstractmethod
189190
def set_deployment_env(self, deployment_env: str) -> None:
190191
"""
191192
Set the deployment environment of the span, identify custom environments.
192193
"""
193194

195+
@abstractmethod
196+
def set_finish_time(self, finish_time: datetime) -> None:
197+
"""
198+
Set the finish time of the span.
199+
"""
200+
194201

195202
class Span(CommonSpanSetter, SpanContext):
196203
"""

examples/trace/simple.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import os
66
import time
7+
from datetime import datetime, timedelta
78

89
import cozeloop
910

@@ -100,6 +101,7 @@ def do_simple_demo():
100101
span.set_error(str(e))
101102

102103
# 3. span finish
104+
span.set_finish_time(datetime.now() + timedelta(seconds=3)) # set finish time as your need to change duration
103105
span.finish()
104106

105107
# 4. (optional) flush or close

0 commit comments

Comments
 (0)