xgae 0.1.9__py3-none-any.whl → 0.1.10__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/__init__.py CHANGED
@@ -0,0 +1,4 @@
1
+ if __name__ == "__main__":
2
+ from xgae.cli_app import main
3
+
4
+ main()
xgae/cli_app.py ADDED
@@ -0,0 +1,87 @@
1
+ import asyncio
2
+ import sys
3
+
4
+ from xgae.engine.mcp_tool_box import XGAMcpToolBox
5
+ from xgae.engine.task_engine import XGATaskEngine
6
+ from xgae.utils.llm_client import LLMConfig
7
+ from xgae.utils.misc import read_file
8
+
9
+ from xgae.utils.setup_env import setup_langfuse, setup_env_logging
10
+
11
+ setup_env_logging()
12
+ langfuse = setup_langfuse()
13
+
14
+ def get_user_message(question)-> str:
15
+ while True:
16
+ user_message = input(f"\n💬 {question}: ")
17
+ if user_message.lower() == 'exit':
18
+ print("\n====== Extreme General Agent Engine CLI EXIT ======")
19
+ sys.exit()
20
+
21
+ if not user_message.strip():
22
+ print("\nuser message is empty, input agin !!!\n")
23
+ continue
24
+
25
+ return user_message
26
+
27
+ async def cli() -> None:
28
+ await asyncio.sleep(1)
29
+ print("\n====== Extreme General Agent Engine CLI START ======")
30
+ user_message = input("\n💬 Start Custom MCP Server and Load User Prompt (Yes/No): ")
31
+ tool_box = None
32
+ system_prompt = None
33
+ general_tools = []
34
+ custom_tools = []
35
+ if user_message.lower() == 'yes':
36
+ print(f"--- Start Custom MCP Server in custom_servers.json")
37
+ print(f"--- Load User Prompt in example/fault_user_prompt.txt")
38
+ tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
39
+ system_prompt = read_file("templates/example/fault_user_prompt.txt")
40
+ custom_tools = ["*"]
41
+ else:
42
+ print(f"--- Start General Agent Server")
43
+ print(f"--- Load System Prompt")
44
+ general_tools = ["*"]
45
+
46
+ while True:
47
+ user_message = get_user_message("Enter your message (or 'exit' to quit)")
48
+
49
+ print("\n🔄 Running XGA Engine ...\n")
50
+ engine = XGATaskEngine(tool_box=tool_box,
51
+ general_tools=general_tools,
52
+ custom_tools=custom_tools,
53
+ llm_config=LLMConfig(stream=False),
54
+ system_prompt=system_prompt,
55
+ max_auto_run=8)
56
+
57
+ # Two task run in same langfuse trace
58
+ trace_id = langfuse.trace(name="xgae_cli").trace_id
59
+
60
+ final_result = await engine.run_task_with_final_answer(
61
+ task_message={"role": "user", "content": user_message},
62
+ trace_id=trace_id
63
+ )
64
+
65
+ if final_result["type"] == "ask":
66
+ await asyncio.sleep(1)
67
+ print(f"\n📌 ASK INFO: {final_result['content']}")
68
+ user_message = get_user_message("Enter ASK information (or 'exit' to quit)")
69
+ final_result = await engine.run_task_with_final_answer(
70
+ task_message={"role": "user", "content": user_message},
71
+ trace_id=trace_id
72
+ )
73
+
74
+ await asyncio.sleep(1)
75
+ result_prefix = "✅" if final_result["type"] == "answer" else "❌"
76
+ if final_result["type"] == "ask":
77
+ print("\n *** IMPORTANT: XGA CLI only support showing ONE TURN ASK !")
78
+ result_prefix = "⚠️"
79
+ print(f"\n {result_prefix} FINAL RESULT: {final_result['content']}")
80
+
81
+
82
+ def main():
83
+ asyncio.run(cli())
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
@@ -82,6 +82,9 @@ class NonStreamTaskResponser(TaskResponseProcessor):
82
82
  finish_reason = "completed"
83
83
  break
84
84
  tool_index += 1
85
+ else:
86
+ finish_reason = "non_tool_call"
87
+ logging.warning(f"NonStreamTask: tool_calls is empty, No Tool need to call !")
85
88
 
86
89
  if finish_reason:
87
90
  finish_content = {"status_type": "finish", "finish_reason": finish_reason}
@@ -117,7 +117,7 @@ class XGATaskEngine:
117
117
 
118
118
  self.task_prompt = self.prompt_builder.build_task_prompt(self.model_name, general_tool_schemas, custom_tool_schemas)
119
119
 
120
- logging.info("*" * 30 + f" XGATaskEngine Task'{self.task_id}' Initialized " + "*" * 30)
120
+ logging.info("*" * 10 + f" XGATaskEngine Task'{self.task_id}' Initialized " + "*" * 10)
121
121
  logging.info(f"model_name={self.model_name}, is_stream={self.is_stream}")
122
122
  logging.info(f"general_tools={general_tools}, custom_tools={custom_tools}")
123
123
 
@@ -159,6 +159,10 @@ class XGATaskEngine:
159
159
  logging.warning(f"run_task_auto: Detected finish_reason='xml_tool_limit_reached', stop auto-continue")
160
160
  auto_continue = False
161
161
  break
162
+ elif finish_reason == 'non_tool_call':
163
+ logging.warning(f"run_task_auto: Detected finish_reason='non_tool_call', stop auto-continue")
164
+ auto_continue = False
165
+ break
162
166
  elif finish_reason == 'stop' or finish_reason == 'length': # 'length' never occur
163
167
  auto_continue = True
164
168
  auto_continue_count += 1
@@ -191,14 +195,14 @@ class XGATaskEngine:
191
195
  }
192
196
  llm_messages.append(temp_assistant_message)
193
197
 
194
- llm_count = continuous_state.get("auto_continue_count")
195
- langfuse_metadata = self.task_langfuse.create_llm_langfuse_meta(llm_count)
198
+ auto_count = continuous_state.get("auto_continue_count")
199
+ langfuse_metadata = self.task_langfuse.create_llm_langfuse_meta(auto_count)
196
200
 
197
201
  llm_response = await self.llm_client.create_completion(llm_messages, langfuse_metadata)
198
202
  response_processor = self._create_response_processer()
199
203
 
200
204
  async for chunk in response_processor.process_response(llm_response, llm_messages, continuous_state):
201
- self._logging_reponse_chunk(chunk)
205
+ self._logging_reponse_chunk(chunk, auto_count)
202
206
  yield chunk
203
207
 
204
208
  def _parse_final_result(self, chunks: List[Dict[str, Any]]) -> XGATaskResult:
@@ -238,13 +242,21 @@ class XGATaskEngine:
238
242
  result_type = "answer" if success else "error"
239
243
  result_content = f"Task execute '{tool_name}' {result_type}: {output}"
240
244
  final_result = XGATaskResult(type=result_type, content=result_content)
241
- elif chunk_type == "assistant_complete" and finish_reason == 'stop':
245
+ elif chunk_type == "assistant_complete" and finish_reason == 'non_tool_call':
242
246
  assis_content = chunk.get('content', {})
243
247
  result_content = assis_content.get("content", "LLM output is empty")
244
248
  final_result = XGATaskResult(type="answer", content=result_content)
245
249
 
246
250
  if final_result is not None:
247
251
  break
252
+
253
+ if final_result and finish_reason == "completed":
254
+ logging.info(f"✅ FINAL_RESULT: finish_reason={finish_reason}, final_result={final_result}")
255
+ elif final_result is not None:
256
+ logging.warning(f"⚠️ FINAL_RESULT: finish_reason={finish_reason}, final_result={final_result}")
257
+ else:
258
+ logging.warning(f"❌ FINAL_RESULT: LLM Result is EMPTY, finish_reason={finish_reason}")
259
+ final_result = XGATaskResult(type="error", content="LLM has no answer")
248
260
  except Exception as e:
249
261
  logging.error(f"parse_final_result: Final result pass error: {str(e)}")
250
262
  final_result = XGATaskResult(type="error", content="Parse final result failed!")
@@ -328,7 +340,7 @@ class XGATaskEngine:
328
340
  return XGATaskLangFuse(self.session_id, self.task_id, self.task_run_id, self.task_no, self.agent_id)
329
341
 
330
342
 
331
- def _logging_reponse_chunk(self, chunk):
343
+ def _logging_reponse_chunk(self, chunk, auto_count: int) -> None:
332
344
  chunk_type = chunk.get('type')
333
345
  prefix = ""
334
346
 
@@ -342,17 +354,20 @@ class XGATaskEngine:
342
354
  tool_name = tool_execution.get('function_name')
343
355
  prefix = "-" + tool_name
344
356
 
345
- logging.info(f"TASK_RESP_CHUNK[{chunk_type}{prefix}]: {chunk}")
357
+ logging.info(f"TASK_RESP_CHUNK[{auto_count}]<{chunk_type}{prefix}>: {chunk}")
346
358
 
347
359
 
348
360
  if __name__ == "__main__":
349
361
  import asyncio
350
362
  from xgae.utils.misc import read_file
363
+ from xgae.utils.setup_env import setup_logging
364
+
365
+ setup_logging()
351
366
 
352
367
  async def main():
353
368
  # Before Run Exec: uv run custom_fault_tools
354
369
  tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
355
- system_prompt = read_file("templates/example_user_prompt.txt")
370
+ system_prompt = read_file("templates/example/fault_user_prompt.txt")
356
371
  engine = XGATaskEngine(tool_box=tool_box,
357
372
  general_tools=[],
358
373
  custom_tools=["*"],
@@ -2,20 +2,22 @@
2
2
  from typing import Any, Dict, Optional
3
3
  from langfuse import Langfuse
4
4
 
5
- from xgae.utils.setup_env import setup_langfuse, setup_env_logging
5
+ from xgae.utils.setup_env import setup_langfuse
6
6
  from xgae.utils.llm_client import LangfuseMetadata
7
7
  from xgae.engine.engine_base import XGATaskResult
8
8
 
9
- setup_env_logging()
10
- langfuse:Langfuse = setup_langfuse()
11
-
12
9
  class XGATaskLangFuse:
10
+ langfuse: Langfuse = None
11
+
13
12
  def __init__(self,
14
13
  session_id: str,
15
14
  task_id:str,
16
15
  task_run_id: str,
17
16
  task_no: int,
18
17
  agent_id: str) -> None:
18
+ if XGATaskLangFuse.langfuse is None:
19
+ XGATaskLangFuse.langfuse = setup_langfuse()
20
+
19
21
  self.session_id = session_id
20
22
  self.task_id = task_id
21
23
  self.task_run_id = task_run_id
@@ -35,9 +37,9 @@ class XGATaskLangFuse:
35
37
  trace = None
36
38
  if trace_id:
37
39
  self.trace_id = trace_id
38
- trace = langfuse.trace(id=trace_id)
40
+ trace = XGATaskLangFuse.langfuse.trace(id=trace_id)
39
41
  else:
40
- trace = langfuse.trace(name="xga_task_engine")
42
+ trace = XGATaskLangFuse.langfuse.trace(name="xga_task_engine")
41
43
  self.trace_id = trace.id
42
44
 
43
45
  metadata = {"task_id": self.task_id, "session_id": self.session_id, "agent_id": self.agent_id}
xgae/utils/__init__.py CHANGED
@@ -12,4 +12,4 @@ def to_bool(value: str) -> bool:
12
12
  if value is None:
13
13
  return False
14
14
 
15
- return True if value.lower() == "true" else False
15
+ return value.lower() == "true"
xgae/utils/llm_client.py CHANGED
@@ -8,6 +8,7 @@ from typing import Union, Dict, Any, Optional, List, TypedDict
8
8
  from openai import OpenAIError
9
9
  from litellm.utils import ModelResponse, CustomStreamWrapper
10
10
 
11
+ from xgae.utils import to_bool
11
12
  from xgae.utils.setup_env import setup_langfuse
12
13
 
13
14
  class LLMConfig(TypedDict, total=False):
@@ -46,6 +47,7 @@ class LLMClient:
46
47
  def __init__(self, llm_config: LLMConfig=None):
47
48
  litellm.modify_params = True
48
49
  litellm.drop_params = True
50
+
49
51
  self._init_langfuse()
50
52
 
51
53
  llm_config = llm_config or LLMConfig()
@@ -78,22 +80,27 @@ class LLMClient:
78
80
  self.is_stream = llm_config_params['stream']
79
81
 
80
82
  self.lite_llm_params = self._prepare_llm_params(llm_config_params)
81
- logging.info(f"📡 LLMClient initialed : model={self.model_name}, is_stream={self.is_stream}, enable thinking={self.lite_llm_params['enable_thinking']}")
83
+ logging.info(f"=== LLMClient initialed : model={self.model_name}, is_stream={self.is_stream}, enable thinking={self.lite_llm_params['enable_thinking']}")
82
84
 
83
85
  @staticmethod
84
86
  def _init_langfuse():
85
87
  if not LLMClient.langfuse_inited:
86
88
  LLMClient.langfuse_inited =True
87
- env_langfuse = setup_langfuse()
88
- if env_langfuse and env_langfuse.enabled:
89
- litellm.success_callback = ["langfuse"]
90
- litellm.failure_callback = ["langfuse"]
91
- LLMClient.langfuse_enabled = True
92
- logging.info("=== LiteLLM Langfuse is enable !")
89
+
90
+ env_llm_langfuse_enable = to_bool(os.getenv("LLM_LANGFUSE_ENABLE", False))
91
+ if env_llm_langfuse_enable:
92
+ env_langfuse = setup_langfuse()
93
+ if env_langfuse and env_langfuse.enabled:
94
+ litellm.success_callback = ["langfuse"]
95
+ litellm.failure_callback = ["langfuse"]
96
+ LLMClient.langfuse_enabled = True
97
+ logging.info("🛠️ LiteLLM Langfuse is enable !")
98
+ else:
99
+ LLMClient.langfuse_enabled = False
100
+ logging.warning("🛠️ LiteLLM Langfuse is disable, langfuse.enabled=false !")
93
101
  else:
94
102
  LLMClient.langfuse_enabled = False
95
- logging.warning("*** LiteLLM Langfuse is disable !")
96
-
103
+ logging.warning("🛠️ LiteLLM Langfuse is disable, LLM_LANGFUSE_ENABLE=False !")
97
104
 
98
105
  def _prepare_llm_params(self, llm_config_params: Dict[str, Any]) -> Dict[str, Any]:
99
106
  prepared_llm_params = llm_config_params.copy()
@@ -244,14 +251,16 @@ if __name__ == "__main__":
244
251
  setup_logging()
245
252
  langfuse = setup_langfuse()
246
253
 
247
- async def llm_completion():
254
+ async def main():
248
255
  llm_client = LLMClient(LLMConfig(stream=False))
249
256
 
250
257
  messages = [{"role": "user", "content": "1+1="}]
251
258
  trace_id = langfuse.trace(name = "xgae_litellm_test").trace_id
259
+ await asyncio.sleep(1)
260
+
252
261
  meta = LangfuseMetadata(
253
262
  generation_name="llm_completion_test",
254
- generation_id="generation_id",
263
+ generation_id="generation_id_0",
255
264
  existing_trace_id=trace_id,
256
265
  session_id="session_0",
257
266
  )
@@ -269,7 +278,6 @@ if __name__ == "__main__":
269
278
  else:
270
279
  print(response.choices[0].message.content)
271
280
 
272
-
273
- asyncio.run(llm_completion())
281
+ asyncio.run(main())
274
282
 
275
283
 
xgae/utils/setup_env.py CHANGED
@@ -52,7 +52,7 @@ def setup_logging(log_file: str=None, log_level: str="INFO") :
52
52
 
53
53
  logger.setLevel(logging_level)
54
54
 
55
- logging.info(f"📡 XGAE_LOGGING is initialized, log_level={log_level}, log_file={log_file}")
55
+ logging.info(f"🛠️ XGA_LOGGING is initialized, log_level={log_level}, log_file={log_file}")
56
56
 
57
57
  def setup_env_logging():
58
58
  log_enable = to_bool(os.getenv("LOG_ENABLE", True))
@@ -71,10 +71,10 @@ def setup_langfuse() -> Langfuse:
71
71
  secret_key=env_secret_key,
72
72
  host=env_host)
73
73
 
74
- logging.info("📡 XGAE_LANGFUSE initialized Successfully by Key !")
74
+ logging.info("🛠️ XGA_LANGFUSE initialized Successfully by Key !")
75
75
  else:
76
76
  _langfuse = Langfuse(enabled=False)
77
- logging.warning("📡 XGAE_LANGFUSE Not set key, Langfuse is disabled!")
77
+ logging.warning("🛠️ XGA_LANGFUSE Not set key, Langfuse is disabled!")
78
78
 
79
79
  return _langfuse
80
80
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog==6.9.0
@@ -0,0 +1,21 @@
1
+ xgae/__init__.py,sha256=OEUd9y9AoGBd3xYerdTTpz9xl4NWkmXeq1a2eil7Qro,72
2
+ xgae/cli_app.py,sha256=ePis7gYYZrevEArnCQOhaN7z4C8Y5yJSOIov8z-lGBs,3157
3
+ xgae/engine/engine_base.py,sha256=ioywuTpDMHEmyVcd6BInoU-vR70PhQStE2MVRWoEiJg,1768
4
+ xgae/engine/mcp_tool_box.py,sha256=ZSCBSXRWhISwyZ1uEIbt3esjesM46g-ktv6CxvyPVDU,10030
5
+ xgae/engine/prompt_builder.py,sha256=X9bS7YIms6LYplCpNHeUmi74xFP5MwFXmXNqOt1Xz-Q,4356
6
+ xgae/engine/task_engine.py,sha256=LAo55FKmmO8Jbo5geEUYr8kFCaVigTb-Jm06XuYKYyY,19010
7
+ xgae/engine/task_langfuse.py,sha256=b0aJ_Di-WDcYzi0TFCvcKWxkBz7PYP2jx3N52OptQMs,2349
8
+ xgae/engine/responser/non_stream_responser.py,sha256=9YCCUedbotH-TPPbTh2Mv1qNVYvznHYFPgAnQB7NJSE,6510
9
+ xgae/engine/responser/responser_base.py,sha256=8PcsvQHP68FEhu6v3dT9hDCc_rLKs38i4txWLcJD4ck,29851
10
+ xgae/engine/responser/stream_responser.py,sha256=oPGtrT1nedGMjiBAwPzUlu6Z_rPWeVSODC1xQ6D8cTY,52055
11
+ xgae/tools/without_general_tools_app.py,sha256=FGMV6njcOKwwfitc0j_nUov0RC-eWlhO1IP8_KHz1tQ,3788
12
+ xgae/utils/__init__.py,sha256=_-TTNq5FanrA-jl_w3-4xp-BnRM7SLwfYQcFyvepcW0,332
13
+ xgae/utils/json_helpers.py,sha256=6BkqiyEF3jV3Irb4Z6-wGY2_FNaLlxE1WKlMJHHT6E0,4645
14
+ xgae/utils/llm_client.py,sha256=hvEDb4DBaWVQTXMjXOd6KrFwJFBcI-YXEQD4f_AhG7Q,14008
15
+ xgae/utils/misc.py,sha256=M8lMXYp1pHiY6Ee8ZTUG88GpOAsE5fbYoRO_hcBFUCE,953
16
+ xgae/utils/setup_env.py,sha256=HweQ-WAyxfV3KYjGYi-rRQAbI_SXoimduOLpQPbHfl8,2619
17
+ xgae/utils/xml_tool_parser.py,sha256=I9xAZC_ElwBY19PNUq-WLXe9FSIJMeAv2Xs-VlajI7Y,4782
18
+ xgae-0.1.10.dist-info/METADATA,sha256=SGnhZrr3DDZ600FDMep9ihERmhwFspEtUKRv1THqQsk,310
19
+ xgae-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ xgae-0.1.10.dist-info/entry_points.txt,sha256=vClvL_WBJyF2x3wJCz5CNJ_BJG-dWUh7h2YbAoskHsc,162
21
+ xgae-0.1.10.dist-info/RECORD,,
@@ -1,3 +1,4 @@
1
1
  [console_scripts]
2
2
  custom_fault_tools = examples.tools.custom_fault_tools_app:main
3
+ xgae = xgae.cli_app:main
3
4
  xgae-tools = xgae.tools.without_general_tools_app:main
@@ -1,20 +0,0 @@
1
- xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- xgae/engine/engine_base.py,sha256=ioywuTpDMHEmyVcd6BInoU-vR70PhQStE2MVRWoEiJg,1768
3
- xgae/engine/mcp_tool_box.py,sha256=ZSCBSXRWhISwyZ1uEIbt3esjesM46g-ktv6CxvyPVDU,10030
4
- xgae/engine/prompt_builder.py,sha256=X9bS7YIms6LYplCpNHeUmi74xFP5MwFXmXNqOt1Xz-Q,4356
5
- xgae/engine/task_engine.py,sha256=BLy32wJUbBFGkjAPfHcbSpCUPQm2uZAmLHtZVqvXwx0,18026
6
- xgae/engine/task_langfuse.py,sha256=-WMnlMcBGfDuJpV0ZYKnVzlO0HizGOfFVGyzVkP6Rag,2260
7
- xgae/engine/responser/non_stream_responser.py,sha256=80ZFcitjXpQJANxbYpQ0LlhVzIxmURNJi9Fbo953Uak,6349
8
- xgae/engine/responser/responser_base.py,sha256=8PcsvQHP68FEhu6v3dT9hDCc_rLKs38i4txWLcJD4ck,29851
9
- xgae/engine/responser/stream_responser.py,sha256=oPGtrT1nedGMjiBAwPzUlu6Z_rPWeVSODC1xQ6D8cTY,52055
10
- xgae/tools/without_general_tools_app.py,sha256=FGMV6njcOKwwfitc0j_nUov0RC-eWlhO1IP8_KHz1tQ,3788
11
- xgae/utils/__init__.py,sha256=tMSYguPrubusr7IyKZn6iPYE6qbN7sOsEVDeJPRdPa8,351
12
- xgae/utils/json_helpers.py,sha256=6BkqiyEF3jV3Irb4Z6-wGY2_FNaLlxE1WKlMJHHT6E0,4645
13
- xgae/utils/llm_client.py,sha256=nOhPlllUj4mASdj2dU5HkSmDqYZ8ZMpDa7AKQwUWJnQ,13600
14
- xgae/utils/misc.py,sha256=M8lMXYp1pHiY6Ee8ZTUG88GpOAsE5fbYoRO_hcBFUCE,953
15
- xgae/utils/setup_env.py,sha256=XD6WmGNyMFDKVzNU_ufYtqJNd2Otq4hzLs9YZ2aSz6g,2613
16
- xgae/utils/xml_tool_parser.py,sha256=I9xAZC_ElwBY19PNUq-WLXe9FSIJMeAv2Xs-VlajI7Y,4782
17
- xgae-0.1.9.dist-info/METADATA,sha256=q4_B-_Kt2hswi0kGX9B_H3uI_7C7SWiJOo6HnzoMF8g,309
18
- xgae-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- xgae-0.1.9.dist-info/entry_points.txt,sha256=bYzQ-1iabCduosArMzq1hktJrCjp8jq4_E8Sg8-gEic,137
20
- xgae-0.1.9.dist-info/RECORD,,
File without changes