agenta 0.12.7__py3-none-any.whl → 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of agenta might be problematic. Click here for more details.

Files changed (88) hide show
  1. agenta/__init__.py +3 -1
  2. agenta/cli/helper.py +1 -1
  3. agenta/cli/main.py +1 -1
  4. agenta/cli/variant_commands.py +7 -5
  5. agenta/client/api.py +1 -1
  6. agenta/client/backend/__init__.py +78 -18
  7. agenta/client/backend/client.py +1031 -5526
  8. agenta/client/backend/resources/__init__.py +31 -0
  9. agenta/client/backend/resources/apps/__init__.py +1 -0
  10. agenta/client/backend/resources/apps/client.py +977 -0
  11. agenta/client/backend/resources/bases/__init__.py +1 -0
  12. agenta/client/backend/resources/bases/client.py +127 -0
  13. agenta/client/backend/resources/configs/__init__.py +1 -0
  14. agenta/client/backend/resources/configs/client.py +377 -0
  15. agenta/client/backend/resources/containers/__init__.py +5 -0
  16. agenta/client/backend/resources/containers/client.py +383 -0
  17. agenta/client/backend/resources/containers/types/__init__.py +5 -0
  18. agenta/client/backend/{types → resources/containers/types}/container_templates_response.py +1 -1
  19. agenta/client/backend/resources/environments/__init__.py +1 -0
  20. agenta/client/backend/resources/environments/client.py +131 -0
  21. agenta/client/backend/resources/evaluations/__init__.py +1 -0
  22. agenta/client/backend/resources/evaluations/client.py +1008 -0
  23. agenta/client/backend/resources/evaluators/__init__.py +1 -0
  24. agenta/client/backend/resources/evaluators/client.py +594 -0
  25. agenta/client/backend/resources/observability/__init__.py +1 -0
  26. agenta/client/backend/resources/observability/client.py +1184 -0
  27. agenta/client/backend/resources/testsets/__init__.py +1 -0
  28. agenta/client/backend/resources/testsets/client.py +689 -0
  29. agenta/client/backend/resources/variants/__init__.py +5 -0
  30. agenta/client/backend/resources/variants/client.py +796 -0
  31. agenta/client/backend/resources/variants/types/__init__.py +7 -0
  32. agenta/client/backend/resources/variants/types/add_variant_from_base_and_config_response.py +7 -0
  33. agenta/client/backend/types/__init__.py +54 -22
  34. agenta/client/backend/types/aggregated_result.py +2 -2
  35. agenta/client/backend/types/aggregated_result_evaluator_config.py +9 -0
  36. agenta/client/backend/types/{app_variant_output.py → app_variant_response.py} +4 -2
  37. agenta/client/backend/types/{trace.py → create_span.py} +20 -10
  38. agenta/client/backend/types/create_trace_response.py +37 -0
  39. agenta/client/backend/types/environment_output.py +3 -1
  40. agenta/client/backend/types/environment_output_extended.py +45 -0
  41. agenta/client/backend/types/environment_revision.py +41 -0
  42. agenta/client/backend/types/error.py +37 -0
  43. agenta/client/backend/types/evaluation.py +6 -3
  44. agenta/client/backend/types/evaluation_scenario_output.py +4 -2
  45. agenta/client/backend/types/{delete_evaluation.py → evaluation_scenario_score_update.py} +2 -2
  46. agenta/client/backend/types/evaluation_status_enum.py +4 -0
  47. agenta/client/backend/types/evaluator.py +1 -0
  48. agenta/client/backend/types/{get_config_reponse.py → get_config_response.py} +1 -2
  49. agenta/client/backend/types/human_evaluation_scenario.py +2 -2
  50. agenta/client/backend/types/{app_variant_output_extended.py → human_evaluation_scenario_update.py} +11 -16
  51. agenta/client/backend/types/human_evaluation_update.py +37 -0
  52. agenta/client/backend/types/image.py +1 -0
  53. agenta/client/backend/types/invite_request.py +1 -0
  54. agenta/client/backend/types/{list_api_keys_output.py → list_api_keys_response.py} +1 -1
  55. agenta/client/backend/types/llm_tokens.py +38 -0
  56. agenta/client/backend/types/new_human_evaluation.py +42 -0
  57. agenta/client/backend/types/organization.py +1 -0
  58. agenta/client/backend/types/permission.py +141 -0
  59. agenta/client/backend/types/result.py +2 -0
  60. agenta/client/backend/types/{human_evaluation_scenario_score.py → score.py} +1 -1
  61. agenta/client/backend/types/span.py +18 -16
  62. agenta/client/backend/types/span_detail.py +52 -0
  63. agenta/client/backend/types/span_kind.py +49 -0
  64. agenta/client/backend/types/span_status_code.py +29 -0
  65. agenta/client/backend/types/span_variant.py +38 -0
  66. agenta/client/backend/types/trace_detail.py +52 -0
  67. agenta/client/backend/types/with_pagination.py +40 -0
  68. agenta/client/backend/types/workspace_member_response.py +38 -0
  69. agenta/client/backend/types/workspace_permission.py +40 -0
  70. agenta/client/backend/types/workspace_response.py +44 -0
  71. agenta/client/backend/types/workspace_role.py +41 -0
  72. agenta/client/backend/types/workspace_role_response.py +38 -0
  73. agenta/docker/docker_utils.py +1 -5
  74. agenta/sdk/__init__.py +3 -1
  75. agenta/sdk/agenta_decorator.py +68 -18
  76. agenta/sdk/agenta_init.py +53 -21
  77. agenta/sdk/tracing/context_manager.py +13 -0
  78. agenta/sdk/tracing/decorators.py +41 -0
  79. agenta/sdk/tracing/llm_tracing.py +220 -0
  80. agenta/sdk/tracing/logger.py +19 -0
  81. agenta/sdk/tracing/tasks_manager.py +130 -0
  82. {agenta-0.12.7.dist-info → agenta-0.13.0.dist-info}/METADATA +47 -96
  83. agenta-0.13.0.dist-info/RECORD +161 -0
  84. agenta/client/backend/types/add_variant_from_base_and_config_response.py +0 -7
  85. agenta/client/backend/types/human_evaluation_scenario_update_score.py +0 -5
  86. agenta-0.12.7.dist-info/RECORD +0 -114
  87. {agenta-0.12.7.dist-info → agenta-0.13.0.dist-info}/WHEEL +0 -0
  88. {agenta-0.12.7.dist-info → agenta-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,44 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import datetime as dt
4
+ import typing
5
+
6
+ from ..core.datetime_utils import serialize_datetime
7
+ from .workspace_member_response import WorkspaceMemberResponse
8
+
9
+ try:
10
+ import pydantic.v1 as pydantic # type: ignore
11
+ except ImportError:
12
+ import pydantic # type: ignore
13
+
14
+
15
+ class WorkspaceResponse(pydantic.BaseModel):
16
+ created_at: typing.Optional[dt.datetime]
17
+ updated_at: typing.Optional[dt.datetime]
18
+ id: str
19
+ name: str
20
+ description: typing.Optional[str]
21
+ type: typing.Optional[str]
22
+ organization: str
23
+ members: typing.Optional[typing.List[WorkspaceMemberResponse]]
24
+
25
+ def json(self, **kwargs: typing.Any) -> str:
26
+ kwargs_with_defaults: typing.Any = {
27
+ "by_alias": True,
28
+ "exclude_unset": True,
29
+ **kwargs,
30
+ }
31
+ return super().json(**kwargs_with_defaults)
32
+
33
+ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
34
+ kwargs_with_defaults: typing.Any = {
35
+ "by_alias": True,
36
+ "exclude_unset": True,
37
+ **kwargs,
38
+ }
39
+ return super().dict(**kwargs_with_defaults)
40
+
41
+ class Config:
42
+ frozen = True
43
+ smart_union = True
44
+ json_encoders = {dt.datetime: serialize_datetime}
@@ -0,0 +1,41 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import enum
4
+ import typing
5
+
6
+ T_Result = typing.TypeVar("T_Result")
7
+
8
+
9
+ class WorkspaceRole(str, enum.Enum):
10
+ """
11
+ An enumeration.
12
+ """
13
+
14
+ OWNER = "owner"
15
+ VIEWER = "viewer"
16
+ EDITOR = "editor"
17
+ EVALUATOR = "evaluator"
18
+ WORKSPACE_ADMIN = "workspace_admin"
19
+ DEPLOYMENT_MANAGER = "deployment_manager"
20
+
21
+ def visit(
22
+ self,
23
+ owner: typing.Callable[[], T_Result],
24
+ viewer: typing.Callable[[], T_Result],
25
+ editor: typing.Callable[[], T_Result],
26
+ evaluator: typing.Callable[[], T_Result],
27
+ workspace_admin: typing.Callable[[], T_Result],
28
+ deployment_manager: typing.Callable[[], T_Result],
29
+ ) -> T_Result:
30
+ if self is WorkspaceRole.OWNER:
31
+ return owner()
32
+ if self is WorkspaceRole.VIEWER:
33
+ return viewer()
34
+ if self is WorkspaceRole.EDITOR:
35
+ return editor()
36
+ if self is WorkspaceRole.EVALUATOR:
37
+ return evaluator()
38
+ if self is WorkspaceRole.WORKSPACE_ADMIN:
39
+ return workspace_admin()
40
+ if self is WorkspaceRole.DEPLOYMENT_MANAGER:
41
+ return deployment_manager()
@@ -0,0 +1,38 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import datetime as dt
4
+ import typing
5
+
6
+ from ..core.datetime_utils import serialize_datetime
7
+ from .workspace_role import WorkspaceRole
8
+
9
+ try:
10
+ import pydantic.v1 as pydantic # type: ignore
11
+ except ImportError:
12
+ import pydantic # type: ignore
13
+
14
+
15
+ class WorkspaceRoleResponse(pydantic.BaseModel):
16
+ role_name: WorkspaceRole
17
+ role_description: typing.Optional[str]
18
+
19
+ def json(self, **kwargs: typing.Any) -> str:
20
+ kwargs_with_defaults: typing.Any = {
21
+ "by_alias": True,
22
+ "exclude_unset": True,
23
+ **kwargs,
24
+ }
25
+ return super().json(**kwargs_with_defaults)
26
+
27
+ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
28
+ kwargs_with_defaults: typing.Any = {
29
+ "by_alias": True,
30
+ "exclude_unset": True,
31
+ **kwargs,
32
+ }
33
+ return super().dict(**kwargs_with_defaults)
34
+
35
+ class Config:
36
+ frozen = True
37
+ smart_union = True
38
+ json_encoders = {dt.datetime: serialize_datetime}
@@ -3,11 +3,7 @@ import shutil
3
3
  import tarfile
4
4
  import tempfile
5
5
  from pathlib import Path
6
- from tempfile import TemporaryDirectory
7
6
 
8
- import docker
9
- from agenta.config import settings
10
- from docker.models.images import Image
11
7
 
12
8
  logger = logging.getLogger(__name__)
13
9
  logger.setLevel(logging.DEBUG)
@@ -15,7 +11,7 @@ logger.setLevel(logging.DEBUG)
15
11
  DEBUG = False
16
12
 
17
13
 
18
- def create_dockerfile(out_folder: Path):
14
+ def create_dockerfile(out_folder: Path) -> Path:
19
15
  """Creates a dockerfile based on the template in the out_folder.
20
16
 
21
17
  Arguments:
agenta/sdk/__init__.py CHANGED
@@ -15,7 +15,9 @@ from .types import (
15
15
  FileInputURL,
16
16
  BinaryParam,
17
17
  )
18
- from .agenta_init import Config, init
18
+ from .tracing.decorators import span
19
+ from .agenta_init import Config, init, llm_tracing
19
20
  from .utils.helper.openai_cost import calculate_token_usage
20
21
 
22
+
21
23
  config = PreInitObject("agenta.config", Config)
@@ -5,15 +5,15 @@ import sys
5
5
  import time
6
6
  import inspect
7
7
  import argparse
8
+ import asyncio
8
9
  import traceback
9
10
  import functools
10
11
  from pathlib import Path
11
12
  from tempfile import NamedTemporaryFile
12
- from typing import Any, Callable, Dict, Optional, Tuple, List, Union
13
+ from typing import Any, Callable, Dict, Optional, Tuple, List
13
14
 
14
- from fastapi import Body, FastAPI, UploadFile
15
- from fastapi.responses import JSONResponse
16
15
  from fastapi.middleware.cors import CORSMiddleware
16
+ from fastapi import Body, FastAPI, UploadFile, HTTPException
17
17
 
18
18
  import agenta
19
19
  from .context import save_context
@@ -73,12 +73,34 @@ def entrypoint(func: Callable[..., Any]) -> Callable[..., Any]:
73
73
  config_params = agenta.config.all()
74
74
  ingestible_files = extract_ingestible_files(func_signature)
75
75
 
76
+ # Initialize tracing
77
+ tracing = agenta.llm_tracing()
78
+
76
79
  @functools.wraps(func)
77
80
  async def wrapper(*args, **kwargs) -> Any:
78
81
  func_params, api_config_params = split_kwargs(kwargs, config_params)
82
+
83
+ # Start tracing
84
+ tracing.start_parent_span(
85
+ name=func.__name__,
86
+ inputs=func_params,
87
+ config=config_params,
88
+ environment="playground", # type: ignore #NOTE: wrapper is only called in playground
89
+ )
90
+
91
+ # Ingest files, prepare configurations and run llm app
79
92
  ingest_files(func_params, ingestible_files)
80
93
  agenta.config.set(**api_config_params)
81
- return await execute_function(func, *args, **func_params)
94
+ llm_result = await execute_function(
95
+ func, *args, params=func_params, config_params=config_params
96
+ )
97
+
98
+ # End trace recording
99
+ tracing.end_recording(
100
+ outputs=llm_result.dict(),
101
+ span=tracing.active_trace,
102
+ )
103
+ return llm_result
82
104
 
83
105
  @functools.wraps(func)
84
106
  async def wrapper_deployed(*args, **kwargs) -> Any:
@@ -91,7 +113,27 @@ def entrypoint(func: Callable[..., Any]) -> Callable[..., Any]:
91
113
  agenta.config.pull(config_name=kwargs["config"])
92
114
  else:
93
115
  agenta.config.pull(config_name="default")
94
- return await execute_function(func, *args, **func_params)
116
+
117
+ config = agenta.config.all()
118
+
119
+ # Start tracing
120
+ tracing.start_parent_span(
121
+ name=func.__name__,
122
+ inputs=func_params,
123
+ config=config,
124
+ environment=kwargs["environment"], # type: ignore #NOTE: wrapper is only called in playground
125
+ )
126
+
127
+ llm_result = await execute_function(
128
+ func, *args, params=func_params, config_params=config_params
129
+ )
130
+
131
+ # End trace recording
132
+ tracing.end_recording(
133
+ outputs=llm_result.dict(),
134
+ span=tracing.active_trace,
135
+ )
136
+ return llm_result
95
137
 
96
138
  update_function_signature(wrapper, func_signature, config_params, ingestible_files)
97
139
  route = f"/{endpoint_name}"
@@ -153,9 +195,7 @@ def ingest_files(
153
195
  func_params[name] = ingest_file(func_params[name])
154
196
 
155
197
 
156
- async def execute_function(
157
- func: Callable[..., Any], *args, **func_params
158
- ) -> Union[Dict[str, Any], JSONResponse]:
198
+ async def execute_function(func: Callable[..., Any], *args, **func_params):
159
199
  """Execute the function and handle any exceptions."""
160
200
 
161
201
  try:
@@ -167,30 +207,32 @@ async def execute_function(
167
207
  is_coroutine_function = inspect.iscoroutinefunction(func)
168
208
  start_time = time.perf_counter()
169
209
  if is_coroutine_function:
170
- result = await func(*args, **func_params)
210
+ result = await func(*args, **func_params["params"])
171
211
  else:
172
- result = func(*args, **func_params)
212
+ result = func(*args, **func_params["params"])
213
+
173
214
  end_time = time.perf_counter()
174
215
  latency = end_time - start_time
175
216
 
176
217
  if isinstance(result, Context):
177
218
  save_context(result)
178
219
  if isinstance(result, Dict):
179
- return FuncResponse(**result, latency=round(latency, 4)).dict()
220
+ return FuncResponse(**result, latency=round(latency, 4))
180
221
  if isinstance(result, str):
181
- return FuncResponse(message=result, latency=round(latency, 4)).dict()
222
+ return FuncResponse(message=result, latency=round(latency, 4)) # type: ignore
182
223
  except Exception as e:
183
- return handle_exception(e)
224
+ handle_exception(e)
225
+ return FuncResponse(message="Unexpected error occurred", latency=0) # type: ignore
184
226
 
185
227
 
186
- def handle_exception(e: Exception) -> JSONResponse:
187
- """Handle exceptions and return a JSONResponse."""
228
+ def handle_exception(e: Exception):
229
+ """Handle exceptions."""
188
230
 
189
231
  status_code: int = e.status_code if hasattr(e, "status_code") else 500
190
- traceback_str = traceback.format_exception(e, value=e, tb=e.__traceback__)
191
- return JSONResponse(
232
+ traceback_str = traceback.format_exception(e, value=e, tb=e.__traceback__) # type: ignore
233
+ raise HTTPException(
192
234
  status_code=status_code,
193
- content={"error": str(e), "traceback": "".join(traceback_str)},
235
+ detail={"error": str(e), "traceback": "".join(traceback_str)},
194
236
  )
195
237
 
196
238
 
@@ -356,6 +398,14 @@ def handle_terminal_run(
356
398
  )
357
399
  agenta.config.set(**args_config_params)
358
400
 
401
+ loop = asyncio.get_event_loop()
402
+ result = loop.run_until_complete(
403
+ execute_function(
404
+ func, **{"params": args_func_params, "config_params": args_config_params}
405
+ )
406
+ )
407
+ print(result)
408
+
359
409
 
360
410
  def override_schema(openapi_schema: dict, func_name: str, endpoint: str, params: dict):
361
411
  """
agenta/sdk/agenta_init.py CHANGED
@@ -1,11 +1,14 @@
1
- from agenta.client.exceptions import APIRequestError
2
- from agenta.client.backend.client import AgentaApi
3
1
  import os
4
2
  import logging
5
3
  from typing import Any, Optional
6
4
 
7
5
  from .utils.globals import set_global
8
6
 
7
+ from agenta.client.backend.client import AgentaApi
8
+ from agenta.sdk.tracing.llm_tracing import Tracing
9
+ from agenta.client.exceptions import APIRequestError
10
+
11
+
9
12
  logger = logging.getLogger(__name__)
10
13
  logger.setLevel(logging.DEBUG)
11
14
 
@@ -40,6 +43,7 @@ class AgentaSingleton:
40
43
  base_name: Optional[str] = None,
41
44
  api_key: Optional[str] = None,
42
45
  base_id: Optional[str] = None,
46
+ app_id: Optional[str] = None,
43
47
  host: Optional[str] = None,
44
48
  **kwargs: Any,
45
49
  ) -> None:
@@ -75,30 +79,44 @@ class AgentaSingleton:
75
79
  )
76
80
  else:
77
81
  try:
78
- apps = client.list_apps(app_name=app_name)
79
- if len(apps) == 0:
80
- raise APIRequestError(f"App with name {app_name} not found")
81
-
82
- app_id = apps[0].app_id
83
- if not app_id:
84
- raise APIRequestError(
85
- f"App with name {app_name} does not exist on the server."
86
- )
87
-
88
- bases = client.list_bases(app_id=app_id, base_name=base_name)
89
- if len(bases) == 0:
90
- raise APIRequestError(f"No base was found for the app {app_id}")
91
-
92
- base_id = bases[0].base_id
82
+ app_id = self.get_app(app_name)
83
+ base_id = self.get_app_base(app_id, base_name)
93
84
  except Exception as ex:
94
85
  raise APIRequestError(
95
86
  f"Failed to get base id and/or app_id from the server with error: {ex}"
96
87
  )
88
+
97
89
  self.base_id = base_id
98
90
  self.host = host
91
+ self.app_id = os.environ.get("AGENTA_APP_ID") if app_id is None else app_id
92
+ self.variant_id = os.environ.get("AGENTA_VARIANT_ID")
93
+ self.variant_name = os.environ.get("AGENTA_VARIANT_NAME")
99
94
  self.api_key = api_key
100
95
  self.config = Config(base_id=base_id, host=host)
101
96
 
97
+ def get_app(self, app_name: str) -> str:
98
+ apps = client.apps.list_apps(app_name=app_name)
99
+ if len(apps) == 0:
100
+ raise APIRequestError(f"App with name {app_name} not found")
101
+
102
+ app_id = apps[0].app_id
103
+ return app_id
104
+
105
+ def get_app_base(self, app_id: str, base_name: str) -> str:
106
+ bases = client.bases.list_bases(app_id=app_id, base_name=base_name)
107
+ if len(bases) == 0:
108
+ raise APIRequestError(f"No base was found for the app {app_id}")
109
+ return bases[0].base_id
110
+
111
+ def get_current_config(self):
112
+ """
113
+ Retrieves the current active configuration
114
+ """
115
+
116
+ if self._config_data is None:
117
+ raise RuntimeError("AgentaSingleton has not been initialized")
118
+ return self._config_data
119
+
102
120
 
103
121
  class Config:
104
122
  def __init__(self, base_id, host):
@@ -139,7 +157,7 @@ class Config:
139
157
  if not self.persist:
140
158
  return
141
159
  try:
142
- client.save_config(
160
+ client.configs.save_config(
143
161
  base_id=self.base_id,
144
162
  config_name=config_name,
145
163
  parameters=kwargs,
@@ -161,12 +179,12 @@ class Config:
161
179
  if self.persist:
162
180
  try:
163
181
  if environment_name:
164
- config = client.get_config(
182
+ config = client.configs.get_config(
165
183
  base_id=self.base_id, environment_name=environment_name
166
184
  )
167
185
 
168
186
  else:
169
- config = client.get_config(
187
+ config = client.configs.get_config(
170
188
  base_id=self.base_id,
171
189
  config_name=config_name,
172
190
  )
@@ -176,7 +194,7 @@ class Config:
176
194
  + str(ex)
177
195
  )
178
196
  try:
179
- self.set(**config.parameters)
197
+ self.set(**{"current_version": config.current_version, **config.parameters})
180
198
  except Exception as ex:
181
199
  logger.warning("Failed to set the configuration with error: " + str(ex))
182
200
 
@@ -210,3 +228,17 @@ def init(app_name=None, base_name=None, **kwargs):
210
228
  singleton = AgentaSingleton()
211
229
  singleton.init(app_name=app_name, base_name=base_name, **kwargs)
212
230
  set_global(setup=singleton.setup, config=singleton.config)
231
+
232
+
233
+ def llm_tracing(max_workers: Optional[int] = None) -> Tracing:
234
+ """Function to start llm tracing."""
235
+
236
+ singleton = AgentaSingleton()
237
+ return Tracing(
238
+ base_url=singleton.host,
239
+ app_id=singleton.app_id, # type: ignore
240
+ variant_id=singleton.variant_id, # type: ignore
241
+ variant_name=singleton.variant_name,
242
+ api_key=singleton.api_key,
243
+ max_workers=max_workers,
244
+ )
@@ -0,0 +1,13 @@
1
+ # Own Imports
2
+ from agenta.sdk.tracing.llm_tracing import Tracing
3
+
4
+
5
+ class TracingContextManager:
6
+ def __init__(self, tracing: Tracing):
7
+ ...
8
+
9
+ def __enter__(self):
10
+ ...
11
+
12
+ def __exit__(self, exc_type, exc_val, exc_tb):
13
+ ...
@@ -0,0 +1,41 @@
1
+ # Stdlib Imports
2
+ import inspect
3
+ from functools import wraps
4
+
5
+ # Own Imports
6
+ import agenta as ag
7
+
8
+
9
+ def span(type: str):
10
+ """Decorator to automatically start and end spans."""
11
+
12
+ tracing = ag.llm_tracing()
13
+
14
+ def decorator(func):
15
+ @wraps(func)
16
+ async def wrapper(*args, **kwargs):
17
+ result = None
18
+ span = tracing.start_span(
19
+ name=func.__name__,
20
+ input=kwargs,
21
+ spankind=type,
22
+ )
23
+ try:
24
+ is_coroutine_function = inspect.iscoroutinefunction(func)
25
+ if is_coroutine_function:
26
+ result = await func(*args, **kwargs)
27
+ else:
28
+ result = func(*args, **kwargs)
29
+ tracing.update_span_status(span=span, value="OK")
30
+ except Exception as e:
31
+ result = str(e)
32
+ tracing.update_span_status(span=span, value="ERROR")
33
+ finally:
34
+ if not isinstance(result, dict):
35
+ result = {"message": result}
36
+ tracing.end_span(outputs=result, span=span)
37
+ return result
38
+
39
+ return wrapper
40
+
41
+ return decorator