xgae 0.1.5__py3-none-any.whl → 0.1.7__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 xgae might be problematic. Click here for more details.

xgae/utils/setup_env.py CHANGED
@@ -3,91 +3,78 @@ import os
3
3
 
4
4
  from langfuse import Langfuse
5
5
 
6
- _log_initialized = False
7
-
8
6
  def setup_logging() -> None:
9
- global _log_initialized
10
- if not _log_initialized:
11
- import colorlog
12
- from dotenv import load_dotenv
13
- load_dotenv()
14
-
15
- env_log_level = os.getenv("LOG_LEVEL", "INFO")
16
- env_log_file = os.getenv("LOG_FILE", "log/xga.log")
17
- log_level = getattr(logging, env_log_level.upper(), logging.INFO)
18
-
19
- log_dir = os.path.dirname(env_log_file)
20
- if log_dir and not os.path.exists(log_dir):
21
- os.makedirs(log_dir, exist_ok=True)
22
- else:
23
- os.remove(env_log_file)
24
-
25
- logger = logging.getLogger()
26
- for handler in logger.handlers[:]:
27
- logger.removeHandler(handler)
7
+ import colorlog
8
+ from dotenv import load_dotenv
9
+ load_dotenv()
28
10
 
11
+ env_log_level = os.getenv("LOG_LEVEL", "INFO")
12
+ env_log_file = os.getenv("LOG_FILE", "log/xga.log")
13
+ log_level = getattr(logging, env_log_level.upper(), logging.INFO)
29
14
 
15
+ log_dir = os.path.dirname(env_log_file)
16
+ if log_dir and not os.path.exists(log_dir):
17
+ os.makedirs(log_dir, exist_ok=True)
18
+ else:
19
+ os.remove(env_log_file)
30
20
 
31
- log_colors = {
32
- 'DEBUG': 'cyan',
33
- 'INFO': 'green',
34
- 'WARNING': 'yellow',
35
- 'ERROR': 'red',
36
- 'CRITICAL': 'red,bg_white'
37
- }
21
+ logger = logging.getLogger()
22
+ for handler in logger.handlers[:]:
23
+ logger.removeHandler(handler)
38
24
 
39
- console_formatter = colorlog.ColoredFormatter('%(log_color)s%(asctime)s - %(levelname)-8s%(reset)s %(white)s%(message)s',
40
- log_colors=log_colors,
41
- datefmt='%Y-%m-%d %H:%M:%S'
42
- )
25
+ log_colors = {
26
+ 'DEBUG': 'cyan',
27
+ 'INFO': 'green',
28
+ 'WARNING': 'yellow',
29
+ 'ERROR': 'red',
30
+ 'CRITICAL': 'red,bg_white'
31
+ }
43
32
 
44
- file_formatter = logging.Formatter(
45
- '%(asctime)s -%(levelname)-8s %(message)s',
46
- datefmt='%Y-%m-%d %H:%M:%S'
47
- )
33
+ console_formatter = colorlog.ColoredFormatter('%(log_color)s%(asctime)s - %(levelname)-8s%(reset)s %(white)s%(message)s',
34
+ log_colors=log_colors,
35
+ datefmt='%Y-%m-%d %H:%M:%S'
36
+ )
48
37
 
49
- console_handler = logging.StreamHandler()
50
- console_handler.setFormatter(console_formatter)
38
+ file_formatter = logging.Formatter(
39
+ '%(asctime)s -%(levelname)-8s %(message)s',
40
+ datefmt='%Y-%m-%d %H:%M:%S'
41
+ )
51
42
 
52
- file_handler = logging.FileHandler(env_log_file, encoding='utf-8')
53
- file_handler.setFormatter(file_formatter)
43
+ console_handler = logging.StreamHandler()
44
+ console_handler.setFormatter(console_formatter)
54
45
 
55
- logger.addHandler(console_handler)
56
- logger.addHandler(file_handler)
46
+ file_handler = logging.FileHandler(env_log_file, encoding='utf-8')
47
+ file_handler.setFormatter(file_formatter)
57
48
 
58
- logger.setLevel(log_level)
49
+ logger.addHandler(console_handler)
50
+ logger.addHandler(file_handler)
59
51
 
60
- logging.info(f"Logger is initialized, log_level={env_log_level}, log_file={env_log_file}")
52
+ logger.setLevel(log_level)
61
53
 
62
- _log_initialized = True
54
+ logging.info(f"Logger is initialized, log_level={env_log_level}, log_file={env_log_file}")
63
55
 
64
- setup_logging()
65
-
66
- _langfuse_initialized = False
67
56
 
68
57
  def setup_langfuse() -> Langfuse:
69
- global _langfuse_initialized
70
58
  _langfuse = None
71
- if not _langfuse_initialized:
72
- env_public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
73
- env_secret_key = os.getenv("LANGFUSE_SECRET_KEY")
74
- env_host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
75
- if env_public_key and env_secret_key:
76
- _langfuse = Langfuse(tracing_enabled=True,
77
- public_key=env_public_key,
78
- secret_key=env_secret_key,
79
- host=env_host)
80
- logging.info("Langfuse initialized Successfully by Key !")
81
- else:
82
- _langfuse = Langfuse(tracing_enabled=False)
83
- logging.warning("Not set key, Langfuse is disabled!")
84
-
85
- _langfuse_initialized = True
86
- return _langfuse
59
+ env_public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
60
+ env_secret_key = os.getenv("LANGFUSE_SECRET_KEY")
61
+ env_host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
62
+ if env_public_key and env_secret_key:
63
+ _langfuse = Langfuse(tracing_enabled=True,
64
+ public_key=env_public_key,
65
+ secret_key=env_secret_key,
66
+ host=env_host)
67
+
68
+ logging.info("Langfuse initialized Successfully by Key !")
69
+ else:
70
+ _langfuse = Langfuse(tracing_enabled=False)
71
+ logging.warning("Not set key, Langfuse is disabled!")
87
72
 
88
- langfuse: Langfuse = Langfuse if _langfuse_initialized else setup_langfuse()
73
+ return _langfuse
89
74
 
90
75
 
91
76
  if __name__ == "__main__":
77
+ from xgae.utils import langfuse
78
+
92
79
  trace_id = langfuse.create_trace_id()
93
80
  logging.warning(f"trace_id={trace_id}")
@@ -5,14 +5,11 @@ This module provides a reliable XML tool call parsing system that supports
5
5
  the XML format with structured function_calls blocks.
6
6
  """
7
7
 
8
- import re
9
- import xml.etree.ElementTree as ET
10
- from typing import List, Dict, Any, Optional, Tuple
11
- from dataclasses import dataclass
12
8
  import json
13
9
  import logging
14
-
15
- logger = logging.getLogger(__name__)
10
+ import re
11
+ from dataclasses import dataclass
12
+ from typing import List, Dict, Any, Optional, Tuple
16
13
 
17
14
 
18
15
  @dataclass
@@ -85,7 +82,7 @@ class XMLToolParser:
85
82
  if tool_call:
86
83
  tool_calls.append(tool_call)
87
84
  except Exception as e:
88
- logger.error(f"Error parsing invoke block for {function_name}: {e}")
85
+ logging.error(f"Error parsing invoke block for {function_name}: {e}")
89
86
 
90
87
  return tool_calls
91
88
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog>=6.9.0
@@ -0,0 +1,19 @@
1
+ xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ xgae/engine/engine_base.py,sha256=ySERuLy1YWsf-3s0NFKcyTnXQ4g69wR-cQhtnG0OFmU,1747
3
+ xgae/engine/mcp_tool_box.py,sha256=iBfCLWXvbkKWd1JnSgBLA1T1KdSdpEZCrROkC_cXp7g,10132
4
+ xgae/engine/prompt_builder.py,sha256=8_rNRJksn2QLV_K98S0x0qNeHcmxhU0kB_53IZJTGOU,4366
5
+ xgae/engine/task_engine.py,sha256=mjS7oDlJH-1wBohzz1FfFng2nE_K0xL-QaNZDuQss3A,17986
6
+ xgae/engine/responser/non_stream_responser.py,sha256=kOP9kDEhHtrDKMyVnBPWEx0yO-xTvVacfXGJJqFtONU,6395
7
+ xgae/engine/responser/responser_base.py,sha256=rn3-LkS_alZUwdIxPfDGC_zAH_CncSQ2I-euYA6a45w,30524
8
+ xgae/engine/responser/stream_responser.py,sha256=5KzCHApiPplZ-zN_sbbEbSvj2rtvKWBshJKe_-x7RDI,52927
9
+ xgae/tools/without_general_tools_app.py,sha256=QknIF4OW9xvOad8gx-F_sCBwQYXqMalnNFvYvZXkQ_I,3789
10
+ xgae/utils/__init__.py,sha256=jChvD-p_p5gsrCZUVYPUGJs4CS9gIdNFcSOpkRpcM4Y,317
11
+ xgae/utils/json_helpers.py,sha256=K1ja6GJCatrAheW9bEWAYSQbDI42__boBCZgtsv1gtk,4865
12
+ xgae/utils/llm_client.py,sha256=RvID4bL9yZon096uvuoFZPlqAPiHhET9-9qYp6sUERc,12605
13
+ xgae/utils/misc.py,sha256=EK94YesZp8AmRUqWfN-CjTxyEHPWdIIWpFNO17dzm9g,915
14
+ xgae/utils/setup_env.py,sha256=EVk0KG92Sk6ejBxXZbDDr_dc3KM8GFMofMA4HvXqSfM,2409
15
+ xgae/utils/xml_tool_parser.py,sha256=EJ6BjpD4CSdmS_LqViUJ6P8H9GY2R1e4Dh8rLCR6nSE,7474
16
+ xgae-0.1.7.dist-info/METADATA,sha256=AbGuJUOv4574WF9a2nQNaTxuOKEN2K_U7RaACci2PME,309
17
+ xgae-0.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ xgae-0.1.7.dist-info/entry_points.txt,sha256=rhQ9Vksnu8nA78lPTjiJxOCZ5k6sH6s5YNMR68y7C-A,73
19
+ xgae-0.1.7.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ xgae-tools = xgae.tools.without_general_tools_app:main
@@ -1,216 +0,0 @@
1
- import uuid
2
- import logging
3
-
4
- from typing import List, Dict, Any, AsyncGenerator, override,Optional
5
-
6
- from xgae.engine.responser.xga_responser_base import TaskResponseProcessor, TaskResponseContext, TaskRunContinuousState
7
-
8
- from xgae.utils.json_helpers import (
9
- safe_json_parse,
10
- to_json_string, format_for_yield
11
- )
12
-
13
- class NonStreamTaskResponser(TaskResponseProcessor):
14
- def __init__(self, response_context: TaskResponseContext):
15
- super().__init__(response_context)
16
-
17
- @override
18
- async def process_response(self,
19
- llm_response: Any,
20
- prompt_messages: List[Dict[str, Any]],
21
- continuous_state: Optional[TaskRunContinuousState] = None) -> AsyncGenerator[Dict[str, Any], None]:
22
- content = ""
23
- all_tool_data = [] # Stores {'tool_call': ..., 'parsing_details': ...}
24
- tool_index = 0
25
- assistant_message_object = None
26
- tool_result_message_objects = {}
27
- finish_reason = None
28
-
29
- native_tool_calls_for_message = []
30
- llm_model = self.response_context.get("model_name")
31
- thread_id = self.response_context.get("task_id")
32
- thread_run_id = self.response_context.get("task_run_id")
33
- tool_execution_strategy = self.response_context.get("tool_execution_strategy", "parallel")
34
- xml_adding_strategy = self.response_context.get("xml_adding_strategy", "user_message")
35
- max_xml_tool_calls = self.response_context.get("max_xml_tool_calls", 0)
36
- try:
37
- # Save and Yield thread_run_start status message
38
- start_content = {"status_type": "thread_run_start", "thread_run_id": thread_run_id}
39
- start_msg_obj = self.add_message(
40
- type="status", content=start_content,
41
- is_llm_message=False, metadata={"thread_run_id": thread_run_id}
42
- )
43
- # if start_msg_obj: yield format_for_yield(start_msg_obj)
44
-
45
- # Extract finish_reason, content, tool calls
46
- if hasattr(llm_response, 'choices') and llm_response.choices:
47
- if hasattr(llm_response.choices[0], 'finish_reason'):
48
- finish_reason = llm_response.choices[0].finish_reason
49
- logging.info(f"Non-streaming finish_reason: {finish_reason}")
50
- self.trace.event(name="non_streaming_finish_reason", level="DEFAULT",
51
- status_message=(f"Non-streaming finish_reason: {finish_reason}"))
52
- response_message = llm_response.choices[0].message if hasattr(llm_response.choices[0],
53
- 'message') else None
54
- if response_message:
55
- if hasattr(response_message, 'content') and response_message.content:
56
- content = response_message.content
57
-
58
- parsed_xml_data = self._parse_xml_tool_calls(content)
59
- if max_xml_tool_calls > 0 and len(parsed_xml_data) > max_xml_tool_calls:
60
- # Truncate content and tool data if limit exceeded
61
- # ... (Truncation logic similar to streaming) ...
62
- if parsed_xml_data:
63
- xml_chunks = self._extract_xml_chunks(content)[:max_xml_tool_calls]
64
- if xml_chunks:
65
- last_chunk = xml_chunks[-1]
66
- last_chunk_pos = content.find(last_chunk)
67
- if last_chunk_pos >= 0: content = content[:last_chunk_pos + len(last_chunk)]
68
- parsed_xml_data = parsed_xml_data[:max_xml_tool_calls]
69
- finish_reason = "xml_tool_limit_reached"
70
- all_tool_data.extend(parsed_xml_data)
71
-
72
- # if config.native_tool_calling and hasattr(response_message,
73
- # 'tool_calls') and response_message.tool_calls:
74
- # for tool_call in response_message.tool_calls:
75
- # if hasattr(tool_call, 'function'):
76
- # exec_tool_call = {
77
- # "function_name": tool_call.function.name,
78
- # "arguments": safe_json_parse(tool_call.function.arguments) if isinstance(
79
- # tool_call.function.arguments, str) else tool_call.function.arguments,
80
- # "id": tool_call.id if hasattr(tool_call, 'id') else str(uuid.uuid4())
81
- # }
82
- # all_tool_data.append({"tool_call": exec_tool_call, "parsing_details": None})
83
- # native_tool_calls_for_message.append({
84
- # "id": exec_tool_call["id"], "type": "function",
85
- # "function": {
86
- # "name": tool_call.function.name,
87
- # "arguments": tool_call.function.arguments if isinstance(
88
- # tool_call.function.arguments, str) else to_json_string(
89
- # tool_call.function.arguments)
90
- # }
91
- # })
92
-
93
- # --- SAVE and YIELD Final Assistant Message ---
94
- message_data = {"role": "assistant", "content": content,
95
- "tool_calls": native_tool_calls_for_message or None}
96
- assistant_message_object = self._add_message_with_agent_info(type="assistant", content=message_data,
97
- is_llm_message=True, metadata={"thread_run_id": thread_run_id}
98
- )
99
- if assistant_message_object:
100
- yield assistant_message_object
101
- else:
102
- logging.error(f"Failed to save non-streaming assistant message for thread {thread_id}")
103
- self.trace.event(name="failed_to_save_non_streaming_assistant_message_for_thread", level="ERROR",
104
- status_message=(
105
- f"Failed to save non-streaming assistant message for thread {thread_id}"))
106
- err_content = {"role": "system", "status_type": "error", "message": "Failed to save assistant message"}
107
- err_msg_obj = self.add_message(
108
- type="status", content=err_content,
109
- is_llm_message=False, metadata={"thread_run_id": thread_run_id}
110
- )
111
- if err_msg_obj: yield format_for_yield(err_msg_obj)
112
-
113
- # --- Execute Tools and Yield Results ---
114
- tool_calls_to_execute = [item['tool_call'] for item in all_tool_data]
115
- if tool_calls_to_execute:
116
- logging.info(
117
- f"Executing {len(tool_calls_to_execute)} tools with strategy: {tool_execution_strategy}")
118
- self.trace.event(name="executing_tools_with_strategy", level="DEFAULT", status_message=(
119
- f"Executing {len(tool_calls_to_execute)} tools with strategy: {tool_execution_strategy}"))
120
- tool_results = await self._execute_tools(tool_calls_to_execute, tool_execution_strategy)
121
-
122
- for i, (returned_tool_call, result) in enumerate(tool_results):
123
- original_data = all_tool_data[i]
124
- tool_call_from_data = original_data['tool_call']
125
- parsing_details = original_data['parsing_details']
126
- current_assistant_id = assistant_message_object['message_id'] if assistant_message_object else None
127
-
128
- context = self._create_tool_context(
129
- tool_call_from_data, tool_index, current_assistant_id, parsing_details
130
- )
131
- context.result = result
132
-
133
- # Save and Yield start status
134
- started_msg_obj = self._yield_and_save_tool_started(context, thread_id, thread_run_id)
135
- if started_msg_obj: yield format_for_yield(started_msg_obj)
136
-
137
- # Save tool result
138
- saved_tool_result_object = self._add_tool_result(
139
- thread_id, tool_call_from_data, result, xml_adding_strategy,
140
- current_assistant_id, parsing_details
141
- )
142
-
143
- # Save and Yield completed/failed status
144
- completed_msg_obj = self._yield_and_save_tool_completed(
145
- context,
146
- saved_tool_result_object['message_id'] if saved_tool_result_object else None,
147
- thread_id, thread_run_id
148
- )
149
- if completed_msg_obj: yield format_for_yield(completed_msg_obj)
150
-
151
- # Yield the saved tool result object
152
- if saved_tool_result_object:
153
- tool_result_message_objects[tool_index] = saved_tool_result_object
154
- yield format_for_yield(saved_tool_result_object)
155
- else:
156
- logging.error(f"Failed to save tool result for index {tool_index}")
157
- self.trace.event(name="failed_to_save_tool_result_for_index", level="ERROR",
158
- status_message=(f"Failed to save tool result for index {tool_index}"))
159
-
160
- if completed_msg_obj["metadata"].get("agent_should_terminate") == "true":
161
- finish_reason = "completed"
162
- break
163
- tool_index += 1
164
-
165
- # --- Save and Yield Final Status ---
166
- if finish_reason:
167
- finish_content = {"status_type": "finish", "finish_reason": finish_reason}
168
- finish_msg_obj = self.add_message(
169
- type="status", content=finish_content,
170
- is_llm_message=False, metadata={"thread_run_id": thread_run_id}
171
- )
172
- if finish_msg_obj: yield format_for_yield(finish_msg_obj)
173
-
174
- # --- Save and Yield assistant_response_end ---
175
- if assistant_message_object: # Only save if assistant message was saved
176
- try:
177
- # Save the full LiteLLM response object directly in content
178
- self.add_message(
179
- type="assistant_response_end",
180
- content=llm_response,
181
- is_llm_message=False,
182
- metadata={"thread_run_id": thread_run_id}
183
- )
184
- logging.info("Assistant response end saved for non-stream")
185
- except Exception as e:
186
- logging.error(f"Error saving assistant response end for non-stream: {str(e)}")
187
- self.trace.event(name="error_saving_assistant_response_end_for_non_stream", level="ERROR",
188
- status_message=(f"Error saving assistant response end for non-stream: {str(e)}"))
189
-
190
- except Exception as e:
191
- logging.error(f"Error processing non-streaming response: {str(e)}", exc_info=True)
192
- self.trace.event(name="error_processing_non_streaming_response", level="ERROR",
193
- status_message=(f"Error processing non-streaming response: {str(e)}"))
194
- # Save and yield error status
195
- err_content = {"role": "system", "status_type": "error", "message": str(e)}
196
- err_msg_obj = self.add_message(
197
- type="status", content=err_content,
198
- is_llm_message=False, metadata={"thread_run_id": thread_run_id if 'thread_run_id' in locals() else None}
199
- )
200
- if err_msg_obj: yield format_for_yield(err_msg_obj)
201
-
202
- # Re-raise the same exception (not a new one) to ensure proper error propagation
203
- logging.critical(f"Re-raising error to stop further processing: {str(e)}")
204
- self.trace.event(name="re_raising_error_to_stop_further_processing", level="CRITICAL",
205
- status_message=(f"Re-raising error to stop further processing: {str(e)}"))
206
- raise # Use bare 'raise' to preserve the original exception with its traceback
207
-
208
- finally:
209
- # Save and Yield the final thread_run_end status
210
- end_content = {"status_type": "thread_run_end"}
211
- end_msg_obj = self.add_message(
212
- type="status", content=end_content,
213
- is_llm_message=False, metadata={"thread_run_id": thread_run_id if 'thread_run_id' in locals() else None}
214
- )
215
- #if end_msg_obj: yield format_for_yield(end_msg_obj)
216
-
@@ -1,16 +0,0 @@
1
- xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- xgae/engine/xga_base.py,sha256=Es6OICHQ5h9gq5MGm0Mx-7ScyfP94_kVSuRCSFHTsxo,1642
3
- xgae/engine/xga_engine.py,sha256=CVi9vhwI9kjlyqk_KF3AbpLpYcjCuWulZ8C2UD7242w,17501
4
- xgae/engine/xga_mcp_tool_box.py,sha256=o0HpP22DA3VPn1CzuQR-r1ue9GIkWGxjLLphJqc7iHw,9883
5
- xgae/engine/xga_prompt_builder.py,sha256=RuTvQCNufqxDwVvSOPXR0qxAc42cG7NuIaUy9amu66A,4351
6
- xgae/engine/responser/xga_non_stream_responser.py,sha256=qAWQb8VUIiYjcnww4Eap5tLbigGeTc_DO4gskilbiV0,12882
7
- xgae/engine/responser/xga_responser_base.py,sha256=RYaO4hn2EXiO--KgIgNmT-cBWPgTpHMAJT47bpMqdhE,34960
8
- xgae/engine/responser/xga_stream_responser.py,sha256=1x-z1JDghs9SLfSPc1RTS-RMLvs8N3KebaB_l62YIgg,52285
9
- xgae/utils/json_helpers.py,sha256=K1ja6GJCatrAheW9bEWAYSQbDI42__boBCZgtsv1gtk,4865
10
- xgae/utils/llm_client.py,sha256=mgzn8heUyRm92HTLEYGdfsGEpFtD-xLFr39P98_JP0s,12402
11
- xgae/utils/setup_env.py,sha256=-Ehv7_E9udHc8AjP66Y78E4X7_G6gpuNJkioCh5fn4A,2902
12
- xgae/utils/utils.py,sha256=cCYmWjKFksZ8BRD1YYnaM_jTLVHAg1ibEdjsczEUO6k,1134
13
- xgae/utils/xml_tool_parser.py,sha256=7Ei7X8zSgVct0fFCSmxDtknCLtdrUIwL9hy_0qSNlvs,7546
14
- xgae-0.1.5.dist-info/METADATA,sha256=JdXLTZ7vw3S4EAA7heCS5j0Vg1-1mHDPCfskAPSsZjc,309
15
- xgae-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- xgae-0.1.5.dist-info/RECORD,,
File without changes