hivetrace 1.3.15__py3-none-any.whl → 1.4.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.
@@ -2,7 +2,7 @@
2
2
  Base adapter class for HiveTrace integrations.
3
3
  """
4
4
 
5
- from typing import Any, Dict, Optional
5
+ from typing import Any, Coroutine, Dict, Optional, cast
6
6
 
7
7
 
8
8
  class BaseAdapter:
@@ -115,7 +115,13 @@ class BaseAdapter:
115
115
  maybe_coro = actual_log_method()
116
116
 
117
117
  if inspect.isawaitable(maybe_coro):
118
- asyncio.create_task(maybe_coro)
118
+ if inspect.iscoroutine(maybe_coro):
119
+ asyncio.create_task(cast(Coroutine[Any, Any, Any], maybe_coro))
120
+ else:
121
+ async def _await_and_drop() -> None:
122
+ await maybe_coro
123
+
124
+ asyncio.create_task(_await_and_drop())
119
125
  else:
120
126
  # If the method is unexpectedly sync in async mode, call directly
121
127
  # to avoid dropping the log.
@@ -28,15 +28,15 @@ class CrewAIAdapter(BaseAdapter):
28
28
  ):
29
29
  super().__init__(hivetrace, application_id, user_id, session_id)
30
30
  self.agent_id_mapping = agent_id_mapping or {}
31
- self.agents_info = {}
32
- self._runtime_user_id = None
33
- self._runtime_session_id = None
34
- self._runtime_agents_conversation_id = None
35
- self._current_parent_agent_id = None
36
- self._last_active_agent_id = None
31
+ self.agents_info: Dict[str, Dict[str, Any]] = {}
32
+ self._runtime_user_id: Optional[str] = None
33
+ self._runtime_session_id: Optional[str] = None
34
+ self._runtime_agents_conversation_id: Optional[str] = None
35
+ self._current_parent_agent_id: Optional[str] = None
36
+ self._last_active_agent_id: Optional[str] = None
37
37
  self._conversation_started = False
38
38
  self._first_agent_logged = False
39
- self._recent_messages = []
39
+ self._recent_messages: list[int] = []
40
40
  self._max_recent_messages = 5
41
41
 
42
42
  def _reset_conversation_state(self):
@@ -35,6 +35,9 @@ def trace(
35
35
  "Use @trace(hivetrace=your_instance) instead of @trace"
36
36
  )
37
37
 
38
+ if application_id is None:
39
+ raise ValueError("application_id is required")
40
+
38
41
  adapter = CrewAIAdapter(
39
42
  hivetrace=hivetrace,
40
43
  application_id=application_id,
@@ -2,7 +2,7 @@
2
2
  Monitored Agent implementation for CrewAI.
3
3
  """
4
4
 
5
- from typing import Any, Callable
5
+ from typing import Any, Callable, Optional
6
6
 
7
7
  from crewai import Agent, Task
8
8
 
@@ -37,7 +37,7 @@ class MonitoredAgent(Agent):
37
37
  self._adapter_instance = adapter_instance
38
38
  self.callback_func = callback_func
39
39
  self.agent_id = agent_id
40
- self._last_thought = None
40
+ self._last_thought: Optional[str] = None
41
41
 
42
42
  def execute_task(self, task: Task) -> str:
43
43
  """
@@ -1,7 +1,7 @@
1
1
  """Tool wrapping utilities for CrewAI adapter."""
2
2
 
3
3
  import functools
4
- from typing import Any, Callable
4
+ from typing import Any, Callable, cast
5
5
 
6
6
  from hivetrace.utils.uuid_generator import generate_uuid
7
7
 
@@ -58,12 +58,13 @@ def wrap_tool(tool: Any, agent_role: str, adapter_instance) -> Any:
58
58
  if not (hasattr(tool, "_run") and callable(tool._run)):
59
59
  return tool
60
60
 
61
- if getattr(tool._run, "_is_hivetrace_wrapped", False):
61
+ run_callable = cast(Any, tool._run)
62
+ if getattr(run_callable, "_is_hivetrace_wrapped", False):
62
63
  return tool
63
64
 
64
65
  tool_name = getattr(tool, "name", "unknown_tool")
65
66
  wrapped = wrap_tool_function(tool._run, tool_name, agent_role, adapter_instance)
66
- wrapped._is_hivetrace_wrapped = True
67
+ setattr(cast(Any, wrapped), "_is_hivetrace_wrapped", True)
67
68
  tool._run = wrapped
68
69
 
69
70
  return tool
@@ -11,21 +11,33 @@ class OpenaiAgentsAdapter(BaseAdapter):
11
11
  def log_traces(self, trace_calls: dict[str, Call | None], conversation_uuid: str):
12
12
  _trace_calls = self._join_handoff_spans(trace_calls)
13
13
  _trace_calls = self._join_agent_calling_tool_spans(_trace_calls)
14
- source_agent = _trace_calls[list(_trace_calls.keys())[0]]
14
+ source_agent: AgentCall | None = None
15
+ for call in _trace_calls.values():
16
+ if isinstance(call, AgentCall):
17
+ source_agent = call
18
+ break
19
+ if source_agent is None:
20
+ return
21
+
15
22
  self._log_start_message(source_agent, conversation_uuid)
16
23
 
17
24
  for trace_call in _trace_calls.values():
18
25
  if trace_call is None or trace_call.span_parent_id is None:
19
26
  continue
20
27
 
21
- parent_agent: AgentCall = _trace_calls[trace_call.span_parent_id]
22
- if trace_call.type == "agent":
28
+ parent = _trace_calls.get(trace_call.span_parent_id)
29
+ if not isinstance(parent, AgentCall):
30
+ continue
31
+
32
+ if isinstance(trace_call, AgentCall):
33
+ if trace_call.output is None:
34
+ continue
23
35
  additional_params = {
24
36
  "agent_conversation_id": conversation_uuid,
25
37
  "is_final_answer": False,
26
38
  "agents": {
27
39
  trace_call.agent_uuid: {
28
- "agent_parent_id": parent_agent.agent_uuid,
40
+ "agent_parent_id": parent.agent_uuid,
29
41
  "name": trace_call.name,
30
42
  "description": trace_call.instructions,
31
43
  },
@@ -37,6 +49,8 @@ class OpenaiAgentsAdapter(BaseAdapter):
37
49
  )
38
50
 
39
51
  elif trace_call.type == "tool":
52
+ if trace_call.output is None:
53
+ continue
40
54
  self._prepare_and_log(
41
55
  log_method_name_stem="function_call",
42
56
  is_async=False,
@@ -49,9 +63,9 @@ class OpenaiAgentsAdapter(BaseAdapter):
49
63
  "additional_parameters": {
50
64
  "agent_conversation_id": conversation_uuid,
51
65
  "agents": {
52
- parent_agent.agent_uuid: {
53
- "name": parent_agent.name,
54
- "description": parent_agent.instructions,
66
+ parent.agent_uuid: {
67
+ "name": parent.name,
68
+ "description": parent.instructions,
55
69
  },
56
70
  },
57
71
  },
@@ -63,15 +77,19 @@ class OpenaiAgentsAdapter(BaseAdapter):
63
77
  self, trace_calls: dict[str, Call | None]
64
78
  ) -> dict[str, Call | None]:
65
79
  for span_id, span in trace_calls.items():
80
+ if span is None:
81
+ continue
66
82
  if span.type == "agent" and span.span_parent_id is not None:
67
83
  parent = trace_calls[span.span_parent_id]
84
+ if parent is None:
85
+ continue
68
86
  if parent.type == "tool":
69
87
  trace_calls[span.span_parent_id] = None
70
- trace_calls[span_id].span_parent_id = parent.span_parent_id
71
- trace_calls[span_id].input = (
88
+ span.span_parent_id = parent.span_parent_id
89
+ span.input = (
72
90
  parent.input if span.input is None else span.input
73
91
  )
74
- trace_calls[span_id].output = (
92
+ span.output = (
75
93
  parent.output if span.output is None else span.output
76
94
  )
77
95
  return trace_calls
@@ -80,18 +98,22 @@ class OpenaiAgentsAdapter(BaseAdapter):
80
98
  self, trace_calls: dict[str, Call | None]
81
99
  ) -> dict[str, Call | None]:
82
100
  for span in reversed(trace_calls.values()):
101
+ if span is None:
102
+ continue
83
103
  if span.type == "handoff" and span.span_parent_id is not None:
84
104
  parent = trace_calls[span.span_parent_id]
85
105
  child = next(
86
106
  (
87
107
  call
88
108
  for call in trace_calls.values()
89
- if call.name == span.to_agent
109
+ if call is not None and call.name == span.to_agent
90
110
  ),
91
111
  None,
92
112
  )
93
113
  if parent is None:
94
114
  continue
115
+ if child is None:
116
+ continue
95
117
  child.span_parent_id = span.span_parent_id
96
118
  if parent.output is None:
97
119
  parent.output = child.output
@@ -100,6 +122,8 @@ class OpenaiAgentsAdapter(BaseAdapter):
100
122
  return trace_calls
101
123
 
102
124
  def _log_start_message(self, trace_call: AgentCall, conversation_uuid: str):
125
+ if trace_call.input is None:
126
+ return
103
127
  self.input(
104
128
  message=trace_call.input,
105
129
  additional_params={
@@ -114,6 +138,8 @@ class OpenaiAgentsAdapter(BaseAdapter):
114
138
  )
115
139
 
116
140
  def _log_final_message(self, trace_call: AgentCall, conversation_uuid: str):
141
+ if trace_call.output is None:
142
+ return
117
143
  self.output(
118
144
  message=trace_call.output,
119
145
  additional_params={
@@ -121,8 +121,10 @@ class HivetraceOpenAIAgentProcessor(TracingProcessor):
121
121
  isinstance(span.span_data, FunctionSpanData)
122
122
  and span.span_data.type == "function"
123
123
  ):
124
- self._trace_calls[span.span_id].input = span.span_data.input
125
- self._trace_calls[span.span_id].output = span.span_data.output
124
+ call = self._trace_calls.get(span.span_id)
125
+ if call is not None:
126
+ call.input = span.span_data.input
127
+ call.output = span.span_data.output
126
128
 
127
129
  # Save input and output for agent
128
130
  elif (
@@ -133,15 +135,16 @@ class HivetraceOpenAIAgentProcessor(TracingProcessor):
133
135
  if not response or not response.output:
134
136
  return
135
137
  if isinstance(response.output[0], ResponseOutputMessage):
136
- self._trace_calls[span.parent_id].input = span.span_data.input[0][
137
- "content"
138
- ]
139
- self._trace_calls[span.parent_id].output = (
140
- response.output[0].content[0].text
141
- )
142
- self._trace_calls[span.parent_id].instructions = response.instructions
138
+ parent_call = self._trace_calls.get(span.parent_id)
139
+ if parent_call is None:
140
+ return
141
+ parent_call.input = span.span_data.input[0]["content"]
142
+ parent_call.output = response.output[0].content[0].text
143
+ parent_call.instructions = response.instructions
143
144
  elif isinstance(response.output[0], ResponseFunctionToolCall):
144
- self._trace_calls[span.parent_id].instructions = response.instructions
145
+ parent_call = self._trace_calls.get(span.parent_id)
146
+ if parent_call is not None:
147
+ parent_call.instructions = response.instructions
145
148
 
146
149
  def shutdown(self):
147
150
  self._trace_calls = {}
@@ -30,17 +30,17 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
30
30
  async def __aexit__(self, exc_type, exc_val, exc_tb):
31
31
  await self.close()
32
32
 
33
- async def _send_request(
34
- self, endpoint: str, payload: Dict[str, Any]
33
+ async def _post_and_parse(
34
+ self, request_args: Dict[str, Any], endpoint: str
35
35
  ) -> HivetraceResponse:
36
- request_args = self._build_request_args(endpoint, payload)
36
+ """
37
+ Sends a POST request and parses the response.
38
+ """
37
39
  try:
38
40
  response = await self.session.post(**request_args)
39
41
  response.raise_for_status()
40
-
41
42
  api_data = response.json()
42
- return ResponseBuilder.build_response_from_api(api_data)
43
-
43
+ return ResponseBuilder.build_response_from_api(api_data, endpoint=endpoint)
44
44
  except httpx.HTTPStatusError as e:
45
45
  return ErrorHandler.handle_http_error(e)
46
46
  except httpx.ConnectError as e:
@@ -54,40 +54,17 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
54
54
  except Exception as e:
55
55
  return ErrorHandler.handle_unexpected_error(e)
56
56
 
57
+ async def _send_request(
58
+ self, endpoint: str, payload: Dict[str, Any]
59
+ ) -> HivetraceResponse:
60
+ request_args = self._build_request_args(endpoint, payload)
61
+ return await self._post_and_parse(request_args, endpoint)
62
+
57
63
  async def _send_files(
58
64
  self, endpoint: str, files: List[Tuple[str, bytes, str]]
59
65
  ) -> HivetraceResponse:
60
66
  request_args = self._build_files_request_args(endpoint, files)
61
- try:
62
- response = await self.session.post(**request_args)
63
- response.raise_for_status()
64
- api_data = response.json()
65
- return ResponseBuilder.build_response_from_api(api_data)
66
- except httpx.HTTPStatusError as e:
67
- return ErrorHandler.handle_http_error(e)
68
- except httpx.ConnectError as e:
69
- return ErrorHandler.handle_connection_error(e)
70
- except httpx.TimeoutException as e:
71
- return ErrorHandler.handle_timeout_error(e)
72
- except httpx.RequestError as e:
73
- return ErrorHandler.handle_request_error(e)
74
- except ValueError as e:
75
- return ErrorHandler.handle_json_decode_error(e)
76
- except Exception as e:
77
- return ErrorHandler.handle_unexpected_error(e)
78
-
79
- async def _get_blocking_status(self, endpoint: str) -> Optional[bool]:
80
- url = f"{self.hivetrace_url}/{endpoint.lstrip('/')}"
81
- headers = {"Authorization": f"Bearer {self.hivetrace_access_token}"}
82
- try:
83
- response = await self.session.get(
84
- url, headers=headers, timeout=self._DEFAULT_TIMEOUT
85
- )
86
- response.raise_for_status()
87
- data = response.json()
88
- return data.get("blocked")
89
- except Exception:
90
- return None
67
+ return await self._post_and_parse(request_args, endpoint)
91
68
 
92
69
  async def input(
93
70
  self,
@@ -100,22 +77,15 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
100
77
  application_id, message, additional_parameters
101
78
  )
102
79
  process_response = await self._send_request("/process_request/", payload)
103
-
80
+
104
81
  analysis_id = self._extract_analysis_id(process_response)
105
82
  if analysis_id:
106
83
  if files:
107
84
  files_response = await self._send_files(
108
85
  f"/user_prompt_analysis/{analysis_id}/attach_files", files
109
86
  )
110
- if hasattr(files_response, "success") and not files_response.success:
111
- return files_response
112
- elif isinstance(files_response, dict) and not files_response.get("success", True):
87
+ if self._is_failure_response(files_response):
113
88
  return files_response
114
-
115
- blocked = await self._get_blocking_status(
116
- f"/user_prompt_analysis/{analysis_id}/check_blocking"
117
- )
118
- self._set_blocked(process_response, blocked)
119
89
  return process_response
120
90
 
121
91
  async def output(
@@ -129,22 +99,15 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
129
99
  application_id, message, additional_parameters
130
100
  )
131
101
  process_response = await self._send_request("/process_response/", payload)
132
-
102
+
133
103
  analysis_id = self._extract_analysis_id(process_response)
134
104
  if analysis_id:
135
105
  if files:
136
106
  files_response = await self._send_files(
137
107
  f"/llm_response_analysis/{analysis_id}/attach_files", files
138
108
  )
139
- if hasattr(files_response, "success") and not files_response.success:
109
+ if self._is_failure_response(files_response):
140
110
  return files_response
141
- elif isinstance(files_response, dict) and not files_response.get("success", True):
142
- return files_response
143
-
144
- blocked = await self._get_blocking_status(
145
- f"/llm_response_analysis/{analysis_id}/check_blocking"
146
- )
147
- self._set_blocked(process_response, blocked)
148
111
  return process_response
149
112
 
150
113
  async def function_call(
hivetrace/client/base.py CHANGED
@@ -1,13 +1,11 @@
1
1
  import os
2
- import uuid
3
2
  from abc import ABC, abstractmethod
4
- from typing import Any, Dict, List, Optional, Tuple, Union
3
+ from typing import Any, Awaitable, Dict, List, Optional, Tuple, Union
5
4
 
6
5
  import httpx
7
6
  from pydantic import ValidationError
8
7
 
9
8
  from ..errors import InvalidParameterError, MissingConfigError
10
- from ..handlers import ErrorHandler
11
9
  from ..models import (
12
10
  FunctionCallRequest,
13
11
  HivetraceResponse,
@@ -54,34 +52,6 @@ class BaseHivetraceSDK(ABC):
54
52
 
55
53
  return value
56
54
 
57
- @staticmethod
58
- def _validate_application_id(application_id: str) -> str:
59
- try:
60
- return str(uuid.UUID(application_id))
61
- except ValueError as e:
62
- raise InvalidParameterError(
63
- parameter="application_id", message="Invalid application_id format"
64
- ) from e
65
-
66
- @staticmethod
67
- def _validate_message(message: str) -> None:
68
- if not isinstance(message, str) or not message.strip():
69
- raise InvalidParameterError(
70
- parameter="message", message="Message must be non-empty"
71
- )
72
-
73
- @staticmethod
74
- def _validate_additional_parameters(
75
- additional_parameters: Optional[Dict[str, Any]],
76
- ) -> None:
77
- if additional_parameters is not None and not isinstance(
78
- additional_parameters, dict
79
- ):
80
- raise InvalidParameterError(
81
- parameter="additional_parameters",
82
- message="Additional parameters must be a dict or None",
83
- )
84
-
85
55
  def _build_request_args(
86
56
  self, endpoint: str, payload: Dict[str, Any]
87
57
  ) -> Dict[str, Any]:
@@ -137,21 +107,29 @@ class BaseHivetraceSDK(ABC):
137
107
  return None
138
108
 
139
109
  @staticmethod
140
- def _set_blocked(response: Any, blocked: Optional[bool]) -> Any:
141
- """Sets 'blocked' flag on response when possible."""
110
+ def _is_failure_response(response: Any) -> bool:
142
111
  try:
143
112
  if isinstance(response, dict):
144
- response["blocked"] = blocked
145
- return response
146
- if hasattr(response, "blocked"):
147
- setattr(response, "blocked", blocked)
148
- return response
113
+ if response.get("success") is False:
114
+ return True
115
+ status = response.get("status")
116
+ if isinstance(status, str) and "success" not in status.lower():
117
+ return True
118
+ if response.get("error") or response.get("error_type"):
119
+ return True
120
+ return False
121
+
122
+ if hasattr(response, "success") and getattr(response, "success") is False:
123
+ return True
124
+ status = getattr(response, "status", None)
125
+ if isinstance(status, str) and "success" not in status.lower():
126
+ return True
127
+ if getattr(response, "error", None) or getattr(response, "error_type", None):
128
+ return True
149
129
  except Exception:
150
- return response
151
- return response
130
+ return False
152
131
 
153
- def _handle_http_error(self, error: httpx.HTTPStatusError) -> HivetraceResponse:
154
- return ErrorHandler.handle_http_error(error)
132
+ return False
155
133
 
156
134
  def _build_message_payload(
157
135
  self,
@@ -166,7 +144,7 @@ class BaseHivetraceSDK(ABC):
166
144
  message=message,
167
145
  additional_parameters=additional_parameters,
168
146
  )
169
- return request_model.dict()
147
+ return request_model.model_dump()
170
148
  except ValidationError as e:
171
149
  raise InvalidParameterError(
172
150
  parameter="request_data", message=f"Validation failed: {e}"
@@ -191,7 +169,7 @@ class BaseHivetraceSDK(ABC):
191
169
  func_result=func_result,
192
170
  additional_parameters=additional_parameters,
193
171
  )
194
- return request_model.dict()
172
+ return request_model.model_dump()
195
173
  except ValidationError as e:
196
174
  raise InvalidParameterError(
197
175
  parameter="request_data", message=f"Validation failed: {e}"
@@ -203,7 +181,8 @@ class BaseHivetraceSDK(ABC):
203
181
  application_id: str,
204
182
  message: str,
205
183
  additional_parameters: Optional[Dict[str, Any]] = None,
206
- ) -> HivetraceResponse:
184
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
185
+ ) -> Union[HivetraceResponse, Awaitable[HivetraceResponse]]:
207
186
  """Sends user request to HiveTrace."""
208
187
  pass
209
188
 
@@ -213,7 +192,8 @@ class BaseHivetraceSDK(ABC):
213
192
  application_id: str,
214
193
  message: str,
215
194
  additional_parameters: Optional[Dict[str, Any]] = None,
216
- ) -> HivetraceResponse:
195
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
196
+ ) -> Union[HivetraceResponse, Awaitable[HivetraceResponse]]:
217
197
  """Sends LLM response to HiveTrace."""
218
198
  pass
219
199
 
@@ -226,11 +206,11 @@ class BaseHivetraceSDK(ABC):
226
206
  func_args: str,
227
207
  func_result: Optional[Union[Dict, str]] = None,
228
208
  additional_parameters: Optional[Dict[str, Any]] = None,
229
- ) -> HivetraceResponse:
209
+ ) -> Union[HivetraceResponse, Awaitable[HivetraceResponse]]:
230
210
  """Sends function call to HiveTrace."""
231
211
  pass
232
212
 
233
213
  @abstractmethod
234
- def close(self) -> None:
214
+ def close(self) -> Union[None, Awaitable[None]]:
235
215
  """Closes HTTP session and frees resources."""
236
216
  pass
@@ -43,17 +43,15 @@ class SyncHivetraceSDK(BaseHivetraceSDK):
43
43
  except Exception:
44
44
  pass
45
45
 
46
- def _send_request(
47
- self, endpoint: str, payload: Dict[str, Any]
48
- ) -> HivetraceResponse:
49
- request_args = self._build_request_args(endpoint, payload)
46
+ def _post_and_parse(self, request_args: Dict[str, Any], endpoint: str) -> HivetraceResponse:
47
+ """
48
+ Sends a POST request and parses the response.
49
+ """
50
50
  try:
51
51
  response = self.session.post(**request_args)
52
52
  response.raise_for_status()
53
-
54
53
  api_data = response.json()
55
- return ResponseBuilder.build_response_from_api(api_data)
56
-
54
+ return ResponseBuilder.build_response_from_api(api_data, endpoint=endpoint)
57
55
  except httpx.HTTPStatusError as e:
58
56
  return ErrorHandler.handle_http_error(e)
59
57
  except httpx.ConnectError as e:
@@ -67,42 +65,17 @@ class SyncHivetraceSDK(BaseHivetraceSDK):
67
65
  except Exception as e:
68
66
  return ErrorHandler.handle_unexpected_error(e)
69
67
 
68
+ def _send_request(
69
+ self, endpoint: str, payload: Dict[str, Any]
70
+ ) -> HivetraceResponse:
71
+ request_args = self._build_request_args(endpoint, payload)
72
+ return self._post_and_parse(request_args, endpoint)
73
+
70
74
  def _send_files(
71
75
  self, endpoint: str, files: List[Tuple[str, bytes, str]]
72
76
  ) -> HivetraceResponse:
73
77
  request_args = self._build_files_request_args(endpoint, files)
74
- try:
75
- response = self.session.post(**request_args)
76
- response.raise_for_status()
77
-
78
- api_data = response.json()
79
- return ResponseBuilder.build_response_from_api(api_data)
80
-
81
- except httpx.HTTPStatusError as e:
82
- return ErrorHandler.handle_http_error(e)
83
- except httpx.ConnectError as e:
84
- return ErrorHandler.handle_connection_error(e)
85
- except httpx.TimeoutException as e:
86
- return ErrorHandler.handle_timeout_error(e)
87
- except httpx.RequestError as e:
88
- return ErrorHandler.handle_request_error(e)
89
- except ValueError as e:
90
- return ErrorHandler.handle_json_decode_error(e)
91
- except Exception as e:
92
- return ErrorHandler.handle_unexpected_error(e)
93
-
94
- def _get_blocking_status(self, endpoint: str) -> Optional[bool]:
95
- url = f"{self.hivetrace_url}/{endpoint.lstrip('/')}"
96
- headers = {"Authorization": f"Bearer {self.hivetrace_access_token}"}
97
- try:
98
- response = self.session.get(
99
- url, headers=headers, timeout=self._DEFAULT_TIMEOUT
100
- )
101
- response.raise_for_status()
102
- data = response.json()
103
- return data.get("blocked")
104
- except Exception:
105
- return None
78
+ return self._post_and_parse(request_args, endpoint)
106
79
 
107
80
  def input(
108
81
  self,
@@ -115,22 +88,15 @@ class SyncHivetraceSDK(BaseHivetraceSDK):
115
88
  application_id, message, additional_parameters
116
89
  )
117
90
  process_response = self._send_request("/process_request/", payload)
118
-
91
+
119
92
  analysis_id = self._extract_analysis_id(process_response)
120
93
  if analysis_id:
121
94
  if files:
122
95
  files_response = self._send_files(
123
96
  f"/user_prompt_analysis/{analysis_id}/attach_files", files
124
97
  )
125
- if hasattr(files_response, "success") and not files_response.success:
126
- return files_response
127
- elif isinstance(files_response, dict) and not files_response.get("success", True):
98
+ if self._is_failure_response(files_response):
128
99
  return files_response
129
-
130
- blocked = self._get_blocking_status(
131
- f"/user_prompt_analysis/{analysis_id}/check_blocking"
132
- )
133
- self._set_blocked(process_response, blocked)
134
100
  return process_response
135
101
 
136
102
  def output(
@@ -144,22 +110,15 @@ class SyncHivetraceSDK(BaseHivetraceSDK):
144
110
  application_id, message, additional_parameters
145
111
  )
146
112
  process_response = self._send_request("/process_response/", payload)
147
-
113
+
148
114
  analysis_id = self._extract_analysis_id(process_response)
149
115
  if analysis_id:
150
116
  if files:
151
117
  files_response = self._send_files(
152
118
  f"/llm_response_analysis/{analysis_id}/attach_files", files
153
119
  )
154
- if hasattr(files_response, "success") and not files_response.success:
155
- return files_response
156
- elif isinstance(files_response, dict) and not files_response.get("success", True):
120
+ if self._is_failure_response(files_response):
157
121
  return files_response
158
-
159
- blocked = self._get_blocking_status(
160
- f"/llm_response_analysis/{analysis_id}/check_blocking"
161
- )
162
- self._set_blocked(process_response, blocked)
163
122
  return process_response
164
123
 
165
124
  def function_call(
hivetrace/errors/api.py CHANGED
@@ -8,7 +8,7 @@ from .base import HiveTraceError
8
8
  class APIError(HiveTraceError):
9
9
  """Базовое исключение для API ошибок."""
10
10
 
11
- def __init__(self, message: str, status_code: int = None):
11
+ def __init__(self, message: str, status_code: int | None = None):
12
12
  super().__init__(message)
13
13
  self.status_code = status_code
14
14
 
@@ -16,7 +16,7 @@ class APIError(HiveTraceError):
16
16
  class HTTPError(APIError):
17
17
  """Исключение для HTTP ошибок."""
18
18
 
19
- def __init__(self, status_code: int, message: str = None):
19
+ def __init__(self, status_code: int, message: str | None = None):
20
20
  message = message or f"HTTP error {status_code}"
21
21
  super().__init__(message, status_code)
22
22