xgae 0.1.1__py3-none-any.whl → 0.1.3__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/engine/xga_base.py CHANGED
@@ -2,7 +2,9 @@ from typing import Union, Optional, Dict, List, Any, Literal
2
2
  from dataclasses import dataclass
3
3
  from abc import ABC, abstractmethod
4
4
 
5
-
5
+ class XGAError(Exception):
6
+ """Custom exception for errors in the XGA system."""
7
+ pass
6
8
 
7
9
  @dataclass
8
10
  class XGAMessage:
@@ -20,7 +22,8 @@ class XGAToolSchema:
20
22
  tool_name: str
21
23
  server_name: str
22
24
  description: str
23
- input_schema: Optional[str]
25
+ input_schema: Dict[str, Any]
26
+ metadata: Optional[Dict[str, Any]]
24
27
 
25
28
 
26
29
  @dataclass
xgae/engine/xga_engine.py CHANGED
@@ -2,46 +2,74 @@
2
2
  from typing import List, Any, Dict, Optional, AsyncGenerator
3
3
  from uuid import uuid4
4
4
 
5
- from xgae.engine.xga_base import XGAMessage, XGAToolSchema, XGAToolBox
5
+ from xgae.engine.xga_base import XGAMessage, XGAToolBox
6
6
  from xgae.utils.llm_client import LLMClient
7
7
  from xgae.utils.setup_env import langfuse
8
8
  from xga_prompt_builder import XGAPromptBuilder
9
9
  from xga_mcp_tool_box import XGAMcpToolBox
10
10
 
11
- class XGAEngine():
11
+
12
+ class XGATaskEngine():
12
13
  def __init__(self,
13
14
  session_id: Optional[str] = None,
14
- trace_id: Optional[str] = None,
15
+ task_id: Optional[str] = None,
15
16
  agent_id: Optional[str] = None,
17
+ trace_id: Optional[str] = None,
18
+ system_prompt: Optional[str] = None,
16
19
  llm_config: Optional[Dict[str, Any]] = None,
17
20
  prompt_builder: Optional[XGAPromptBuilder] = None,
18
21
  tool_box: Optional[XGAToolBox] = None):
19
22
  self.session_id = session_id if session_id else f"xga_sid_{uuid4()}"
23
+ self.task_id = task_id if task_id else f"xga_task_{uuid4()}"
20
24
  self.agent_id = agent_id
25
+ self.trace_id = trace_id if trace_id else langfuse.create_trace_id()
21
26
 
22
27
  self.messages: List[XGAMessage] = []
23
28
  self.llm_client = LLMClient(llm_config)
24
29
  self.model_name = self.llm_client.model_name
25
30
  self.is_stream = self.llm_client.is_stream
26
31
 
27
- self.prompt_builder = prompt_builder or XGAPromptBuilder()
32
+ self.prompt_builder = prompt_builder or XGAPromptBuilder(system_prompt)
28
33
  self.tool_box = tool_box or XGAMcpToolBox()
29
34
 
30
- self.task_id = None
31
- self.trace_id = trace_id if trace_id else langfuse.create_trace_id()
35
+
36
+ async def __async_init__(self, general_tools:List[str], custom_tools: List[str]) -> None:
37
+ await self.tool_box.load_mcp_tools_schema()
38
+ await self.tool_box.creat_task_tool_box(self.task_id, general_tools, custom_tools)
39
+ general_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
40
+ custom_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "custom_tool")
41
+
42
+ self.task_prompt = self.prompt_builder.build_task_prompt(self.model_name, general_tool_schemas, custom_tool_schemas)
43
+
44
+ @classmethod
45
+ async def create(cls,
46
+ session_id: Optional[str] = None,
47
+ task_id: Optional[str] = None,
48
+ agent_id: Optional[str] = None,
49
+ trace_id: Optional[str] = None,
50
+ system_prompt: Optional[str] = None,
51
+ general_tools: Optional[List[str]] = None,
52
+ custom_tools: Optional[List[str]] = None,
53
+ llm_config: Optional[Dict[str, Any]] = None,
54
+ prompt_builder: Optional[XGAPromptBuilder] = None,
55
+ tool_box: Optional[XGAToolBox] = None) -> 'XGATaskEngine':
56
+ engine: XGATaskEngine = cls(session_id=session_id,
57
+ task_id=task_id,
58
+ agent_id=agent_id,
59
+ trace_id=trace_id,
60
+ system_prompt=system_prompt,
61
+ llm_config=llm_config,
62
+ prompt_builder=prompt_builder,
63
+ tool_box=tool_box)
64
+ general_tools = general_tools or ["*"]
65
+ custom_tools = custom_tools or []
66
+ await engine.__async_init__(general_tools, custom_tools)
67
+ return engine
32
68
 
33
69
 
34
- async def run_task(self,
35
- task_messages: List[Dict[str, Any]],
36
- task_id: Optional[str],
37
- prompt_template: Optional[str] = None,
38
- general_tools: Optional[List[str]] = ["*"],
39
- custom_tools: Optional[List[str]] = []) -> AsyncGenerator:
70
+ async def run_task(self, task_messages: List[Dict[str, Any]]) -> AsyncGenerator:
40
71
  try:
41
- self.task_id = task_id if task_id else f"xga_task_{uuid4()}"
42
- await self.tool_box.creat_task_tool_box(self.task_id, general_tools, custom_tools)
43
- system_prompt = await self._build_system_prompt(prompt_template, general_tools, custom_tools)
44
- yield system_prompt
72
+ yield self.task_prompt
45
73
 
46
74
  finally:
47
75
  await self.tool_box.destroy_task_tool_box(self.task_id)
@@ -50,20 +78,22 @@ class XGAEngine():
50
78
  def _run_task_once(self):
51
79
  pass
52
80
 
53
- async def _build_system_prompt(self, prompt_template: str, general_tools: List[str], custom_tools: List[str]) -> str:
54
- self.task_tool_schemas: Dict[str, XGAToolSchema] = {}
55
- system_prompt = self.prompt_builder.build_system_prompt(self.model_name, prompt_template)
56
-
57
- tool_schemas = await self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
58
- system_prompt = self.prompt_builder.build_general_tool_prompt(self.model_name, system_prompt, tool_schemas)
59
-
60
- tool_schemas = await self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
61
- system_prompt = self.prompt_builder.build_custom_tool_prompt(self.model_name, system_prompt, tool_schemas)
62
-
63
- return system_prompt
64
81
 
65
82
  def add_message(self, message: XGAMessage):
66
83
  message.message_id = f"xga_msg_{uuid4()}"
67
84
  message.session_id = self.session_id
68
85
  message.agent_id = self.agent_id
69
86
  self.messages.append(message)
87
+
88
+ if __name__ == "__main__":
89
+ import asyncio
90
+
91
+ async def main():
92
+ tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
93
+ engine = await XGATaskEngine.create(tool_box=tool_box, custom_tools=["bomc_fault.*"])
94
+ # engine = await XGATaskEngine.create()
95
+
96
+ async for chunk in engine.run_task(task_messages=[{}]):
97
+ print(chunk)
98
+
99
+ asyncio.run(main())
@@ -1,21 +1,22 @@
1
1
  import json
2
2
  import logging
3
3
  import os
4
+
4
5
  from typing import List, Any, Dict, Optional, Literal, override
5
6
 
6
7
  from langchain_mcp_adapters.client import MultiServerMCPClient
7
8
  from langchain_mcp_adapters.tools import load_mcp_tools
8
9
 
9
- from xgae.engine.xga_base import XGAToolSchema, XGAToolBox, XGAToolResult
10
- from xgae.utils.setup_env import XGAError
11
-
10
+ from xgae.engine.xga_base import XGAError, XGAToolSchema, XGAToolBox, XGAToolResult
11
+ from xgae.utils.setup_env import langfuse
12
12
 
13
13
  class XGAMcpToolBox(XGAToolBox):
14
14
  GENERAL_MCP_SERVER_NAME = "xga_general"
15
15
 
16
16
  def __init__(self,
17
- custom_mcp_server_config: Optional[Dict[str, Any]] = None,
18
- custom_mcp_server_file: Optional[str] = None):
17
+ custom_mcp_server_file: Optional[str] = None,
18
+ custom_mcp_server_config: Optional[Dict[str, Any]] = None
19
+ ):
19
20
  general_mcp_server_config = self._load_mcp_servers_config("mcpservers/xga_server.json")
20
21
  tool_box_mcp_server_config = general_mcp_server_config.get("mcpServers", {})
21
22
 
@@ -31,6 +32,8 @@ class XGAMcpToolBox(XGAToolBox):
31
32
  self.mcp_tool_schemas: Dict[str, List[XGAToolSchema]] = {}
32
33
  self.task_tool_schemas: Dict[str, Dict[str,XGAToolSchema]] = {}
33
34
 
35
+ self.is_loaded_tool_schemas = False
36
+
34
37
  @override
35
38
  async def creat_task_tool_box(self, task_id: str, general_tools: List[str], custom_tools: List[str]):
36
39
  task_tool_schemas = {}
@@ -41,7 +44,7 @@ class XGAMcpToolBox(XGAToolBox):
41
44
  for tool_schema in general_tool_schemas:
42
45
  if tool_schema.tool_name in general_tools:
43
46
  task_tool_schemas[tool_schema.tool_name] = tool_schema
44
- task_tool_schemas.pop("end_task")
47
+ task_tool_schemas.pop("end_task", None)
45
48
 
46
49
  for server_tool_name in custom_tools:
47
50
  parts = server_tool_name.split(".")
@@ -122,20 +125,35 @@ class XGAMcpToolBox(XGAToolBox):
122
125
 
123
126
  return result
124
127
 
128
+
125
129
  async def load_mcp_tools_schema(self)-> None:
126
- for server_name in self.mcp_server_names:
127
- self.mcp_tool_schemas[server_name] = []
128
- mcp_tools = await self._mcp_client.get_tools(server_name=server_name)
129
- for tool in mcp_tools:
130
- input_schema = tool.args_schema
131
- if server_name == self.GENERAL_MCP_SERVER_NAME:
132
- input_schema = str(tool.args_schema) # @todo remove task_id param
133
- input_schema_str = str(input_schema) # @todo convert input tool.args_schema
134
- tool_schema = XGAToolSchema(tool_name=tool.name,
135
- server_name=server_name,
136
- description=tool.description,
137
- input_schema=input_schema_str)
138
- self.mcp_tool_schemas[server_name].append(tool_schema)
130
+ if not self.is_loaded_tool_schemas:
131
+ for server_name in self.mcp_server_names:
132
+ self.mcp_tool_schemas[server_name] = []
133
+ mcp_tools = await self._mcp_client.get_tools(server_name=server_name)
134
+
135
+ for tool in mcp_tools:
136
+ input_schema = tool.args_schema
137
+ if server_name == self.GENERAL_MCP_SERVER_NAME:
138
+ input_schema['properties'].pop("task_id", None)
139
+ if 'task_id' in input_schema['required']:
140
+ input_schema['required'].remove('task_id')
141
+ params_properties = input_schema.get("properties", {})
142
+ for param_properties in params_properties.values():
143
+ param_properties.pop("title", None)
144
+
145
+ metadata = tool.metadata or {}
146
+ tool_schema = XGAToolSchema(tool_name=tool.name,
147
+ server_name=server_name,
148
+ description=tool.description,
149
+ input_schema=input_schema,
150
+ metadata=metadata)
151
+ self.mcp_tool_schemas[server_name].append(tool_schema)
152
+ self.is_loaded_tool_schemas = True
153
+
154
+ async def reload_mcp_tools_schema(self) -> None:
155
+ self.is_loaded_tool_schemas = False
156
+ await self.load_mcp_tools_schema()
139
157
 
140
158
  @staticmethod
141
159
  def _load_mcp_servers_config(mcp_config_path: str) -> Dict[str, Any]:
@@ -164,10 +182,11 @@ class XGAMcpToolBox(XGAToolBox):
164
182
  if __name__ == "__main__":
165
183
  import asyncio
166
184
  from dataclasses import asdict
185
+
167
186
  async def main():
168
187
  task_id = "task1"
169
- mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
170
- #mcp_tool_box = XGAMcpToolBox()
188
+ #mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
189
+ mcp_tool_box = XGAMcpToolBox()
171
190
  await mcp_tool_box.load_mcp_tools_schema()
172
191
  await mcp_tool_box.creat_task_tool_box(task_id=task_id, general_tools=["*"], custom_tools=["bomc_fault.*"])
173
192
  tool_schemas = mcp_tool_box.get_task_tool_schemas(task_id, "general_tool")
@@ -189,4 +208,5 @@ if __name__ == "__main__":
189
208
  print(f"call complete result: {result}")
190
209
 
191
210
  await mcp_tool_box.destroy_task_tool_box(task_id)
211
+
192
212
  asyncio.run(main())
@@ -1,38 +1,97 @@
1
+ import json
1
2
  import datetime
2
-
3
3
  from typing import Optional, List
4
4
 
5
- from xga_base import XGAToolSchema
6
- from xgae.utils.setup_env import read_file, XGAError
5
+ from xga_base import XGAToolSchema, XGAError
6
+ from xgae.utils.utils import read_file, format_file_with_args
7
7
 
8
8
 
9
9
  class XGAPromptBuilder():
10
- def __init__(self,
11
- prompt_template: Optional[str] = None,
12
- prompt_template_file: Optional[str] = None):
13
- self.system_prompt_template = None
14
- if prompt_template:
15
- self.system_prompt_template = prompt_template
16
- elif prompt_template_file:
17
- self.system_prompt_template = read_file(prompt_template_file)
18
- else:
19
- _system_prompt_template = read_file("templates/system_prompt_template.txt")
20
- self.system_prompt_template = _system_prompt_template.format(
21
- current_date=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d'),
22
- current_time=datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S'),
23
- current_year=datetime.datetime.now(datetime.timezone.utc).strftime('%Y')
24
- )
10
+ def __init__(self, system_prompt: Optional[str] = None):
11
+ self.system_prompt = system_prompt
25
12
 
13
+ def build_task_prompt(self, model_name: str, general_tool_schemas: List[XGAToolSchema], custom_tool_schemas: List[XGAToolSchema])-> str:
14
+ if self.system_prompt is None:
15
+ self.system_prompt = self._load_default_system_prompt(model_name)
26
16
 
27
- def build_system_prompt(self, model_name:str, prompt_template: Optional[str]=None)-> str:
28
- system_prompt = prompt_template if prompt_template else self.system_prompt_template
17
+ task_prompt = self.system_prompt
29
18
 
30
- return system_prompt
19
+ tool_prompt = self.build_general_tool_prompt(general_tool_schemas)
20
+ task_prompt = task_prompt + "\n" + tool_prompt
21
+
22
+ tool_prompt = self.build_custom_tool_prompt(custom_tool_schemas)
23
+ task_prompt = task_prompt + "\n" + tool_prompt
24
+
25
+ return task_prompt
26
+
27
+ def build_general_tool_prompt(self, tool_schemas:List[XGAToolSchema])-> str:
28
+ tool_prompt = ""
29
+ tool_schemas = tool_schemas or []
30
+ if len(tool_schemas) > 0:
31
+ tool_prompt = read_file("templates/general_tool_prompt_template.txt")
32
+ example_prompt = ""
33
+ openai_schemas = []
34
+ for tool_schema in tool_schemas:
35
+ openai_schema = {}
36
+ openai_schema["type"] = "function"
37
+ openai_function = {}
38
+ openai_schema["function"] = openai_function
39
+
40
+ openai_function["name"] = tool_schema.tool_name
41
+ openai_function["description"] = tool_schema.description if tool_schema.description else 'No description available'
42
+
43
+ openai_parameters = {}
44
+ input_schema = tool_schema.input_schema
45
+ openai_function["parameters"] = openai_parameters
46
+ openai_parameters["type"] = input_schema["type"]
47
+ openai_parameters["properties"] = input_schema.get("properties", {})
48
+ openai_parameters["required"] = input_schema["required"]
49
+
50
+ openai_schemas.append(openai_schema)
51
+
52
+ metadata = tool_schema.metadata or {}
53
+ example = metadata.get("example", None)
54
+ if example:
55
+ example_prompt += f"\n{example}\n"
31
56
 
57
+ schema_prompt = json.dumps(openai_schemas, ensure_ascii=False, indent=2)
58
+ tool_prompt = tool_prompt.format(tool_schemas=schema_prompt, tool_examples=example_prompt)
59
+ return tool_prompt
32
60
 
33
- def build_general_tool_prompt(self, model_name:str, prompt_template: str, tool_schemas:List[XGAToolSchema])-> str:
34
- pass
61
+
62
+ def build_custom_tool_prompt(self, tool_schemas:List[XGAToolSchema])-> str:
63
+ tool_prompt = ""
64
+ tool_schemas = tool_schemas or []
65
+ if len(tool_schemas) > 0:
66
+ tool_prompt = read_file("templates/custom_tool_prompt_template.txt")
67
+ tool_info = ""
68
+ for tool_schema in tool_schemas:
69
+ description = tool_schema.description if tool_schema.description else 'No description available'
70
+ tool_info += f"- **{tool_schema.tool_name}**: {description}\n"
71
+ parameters = tool_schema.input_schema.get("properties", {})
72
+ tool_info += f" Parameters: {parameters}\n"
73
+ tool_prompt = tool_prompt.replace("{tool_schemas}", tool_info)
74
+
75
+ return tool_prompt
76
+
77
+ def _load_default_system_prompt(self, model_name) -> Optional[str]:
78
+ if "gemini-2.5-flash" in model_name.lower() and "gemini-2.5-pro" not in model_name.lower():
79
+ system_prompt_template = read_file("templates/gemini_system_prompt_template.txt")
80
+ else:
81
+ system_prompt_template = read_file("templates/system_prompt_template.txt")
82
+
83
+ system_prompt = format_file_with_args(system_prompt_template, {"datetime": datetime})
84
+
85
+ system_prompt = system_prompt.format(
86
+ current_date=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d'),
87
+ current_time=datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S'),
88
+ current_year=datetime.datetime.now(datetime.timezone.utc).strftime('%Y')
89
+ )
90
+
91
+ if "anthropic" not in model_name.lower():
92
+ sample_response = read_file("templates/system_prompt_response_sample.txt")
93
+ system_prompt = system_prompt + "\n\n <sample_assistant_response>" + sample_response + "</sample_assistant_response>"
94
+
95
+ return system_prompt
35
96
 
36
97
 
37
- def build_custom_tool_prompt(self, model_name:str, prompt_template: str, tool_schemas:List[XGAToolSchema])-> str:
38
- pass
xgae/utils/llm_client.py CHANGED
@@ -9,8 +9,6 @@ from typing import Union, Dict, Any, Optional, List
9
9
  from litellm.utils import ModelResponse, CustomStreamWrapper
10
10
  from openai import OpenAIError
11
11
 
12
- from setup_env import setup_xga_env
13
-
14
12
 
15
13
  class LLMError(Exception):
16
14
  """Base exception for LLM-related errors."""
@@ -20,7 +18,7 @@ class LLMClient:
20
18
  RATE_LIMIT_DELAY = 30
21
19
  RETRY_DELAY = 0.1
22
20
 
23
- def __init__(self, llm_config: Optional[Dict[str, Any]]={}) -> None:
21
+ def __init__(self, llm_config: Optional[Dict[str, Any]]=None) -> None:
24
22
  """
25
23
  Arg: llm_config (Optional[Dict[str, Any]], optional)
26
24
  model: Override default model to use, default set by .env LLM_MODEL
@@ -36,6 +34,7 @@ class LLMClient:
36
34
  reasoning_effort: Optional level of reasoning effort, default is ‘low’
37
35
  top_p: Optional Top-p sampling parameter, default is None
38
36
  """
37
+ llm_config = llm_config or {}
39
38
  litellm.modify_params = True
40
39
  litellm.drop_params = True
41
40
 
@@ -214,8 +213,6 @@ class LLMClient:
214
213
  raise LLMError(f"LLM completion failed after {self.max_retries} attempts !")
215
214
 
216
215
  if __name__ == "__main__":
217
- setup_xga_env()
218
-
219
216
  async def llm_completion():
220
217
  llm_client = LLMClient({
221
218
  "stream": False #default is True
xgae/utils/setup_env.py CHANGED
@@ -1,107 +1,95 @@
1
1
  import logging
2
2
  import os
3
- import traceback
4
3
 
5
4
  from langfuse import Langfuse
6
5
 
6
+ _log_initialized = False
7
7
 
8
- class XGAError(Exception):
9
- """Custom exception for errors in the XGA system."""
10
- pass
8
+ 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()
11
14
 
12
- langfuse: Langfuse = None
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)
13
18
 
14
- def setup_langfuse() -> None:
15
- env_public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
16
- env_secret_key = os.getenv("LANGFUSE_SECRET_KEY")
17
- env_host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
18
- global langfuse
19
- if env_public_key and env_secret_key:
20
- langfuse = Langfuse(tracing_enabled=True,
21
- public_key=env_public_key,
22
- secret_key=env_secret_key,
23
- host=env_host)
24
- logging.info("utils.setup_langfuse: Langfuse initialized!")
25
- else:
26
- langfuse = Langfuse(tracing_enabled=False)
27
- logging.warning("utils.setup_langfuse: Langfuse is disabled!")
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)
28
24
 
29
- def setup_logging() -> None:
30
- env_log_level = os.getenv("LOG_LEVEL", "INFO")
31
- env_log_file = os.getenv("LOG_FILE", "log/xga.log")
32
- log_level = getattr(logging, env_log_level.upper(), logging.INFO)
25
+ logger = logging.getLogger()
26
+ for handler in logger.handlers[:]:
27
+ logger.removeHandler(handler)
33
28
 
34
- log_dir = os.path.dirname(env_log_file)
35
- if log_dir and not os.path.exists(log_dir):
36
- os.makedirs(log_dir, exist_ok=True)
37
- else:
38
- os.remove(env_log_file)
39
29
 
40
- logger = logging.getLogger()
41
- for handler in logger.handlers[:]:
42
- logger.removeHandler(handler)
43
30
 
44
- import colorlog
31
+ log_colors = {
32
+ 'DEBUG': 'cyan',
33
+ 'INFO': 'green',
34
+ 'WARNING': 'yellow',
35
+ 'ERROR': 'red',
36
+ 'CRITICAL': 'red,bg_white'
37
+ }
45
38
 
46
- log_colors = {
47
- 'DEBUG': 'cyan',
48
- 'INFO': 'green',
49
- 'WARNING': 'yellow',
50
- 'ERROR': 'red',
51
- 'CRITICAL': 'red,bg_white'
52
- }
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
+ )
53
43
 
54
- console_formatter = colorlog.ColoredFormatter('%(log_color)s%(asctime)s - %(levelname)-8s%(reset)s %(white)s%(message)s',
55
- log_colors=log_colors,
56
- datefmt='%Y-%m-%d %H:%M:%S'
57
- )
44
+ file_formatter = logging.Formatter(
45
+ '%(asctime)s -%(levelname)-8s %(message)s',
46
+ datefmt='%Y-%m-%d %H:%M:%S'
47
+ )
58
48
 
59
- file_formatter = logging.Formatter(
60
- '%(asctime)s -%(levelname)-8s %(message)s',
61
- datefmt='%Y-%m-%d %H:%M:%S'
62
- )
49
+ console_handler = logging.StreamHandler()
50
+ console_handler.setFormatter(console_formatter)
63
51
 
64
- console_handler = logging.StreamHandler()
65
- console_handler.setFormatter(console_formatter)
52
+ file_handler = logging.FileHandler(env_log_file, encoding='utf-8')
53
+ file_handler.setFormatter(file_formatter)
66
54
 
67
- file_handler = logging.FileHandler(env_log_file, encoding='utf-8')
68
- file_handler.setFormatter(file_formatter)
55
+ logger.addHandler(console_handler)
56
+ logger.addHandler(file_handler)
69
57
 
70
- logger.addHandler(console_handler)
71
- logger.addHandler(file_handler)
58
+ logger.setLevel(log_level)
72
59
 
73
- logger.setLevel(log_level)
60
+ logging.info(f"Logger is initialized, log_level={env_log_level}, log_file={env_log_file}")
74
61
 
75
- logging.info(f"Logger is initialized, log_level={env_log_level}, log_file={env_log_file}")
62
+ _log_initialized = True
76
63
 
64
+ setup_logging()
77
65
 
78
- def handle_error(e: Exception) -> None:
79
- logging.error("An error occurred: %s", str(e))
80
- logging.error("Traceback details:\n%s", traceback.format_exc())
81
- raise (e) from e
66
+ _langfuse_initialized = False
82
67
 
83
- def read_file(file_path: str) -> str:
84
- if not os.path.exists(file_path):
85
- logging.error(f"File '{file_path}' not found")
86
- raise XGAError(f"File '{file_path}' not found")
68
+ def setup_langfuse() -> Langfuse:
69
+ global _langfuse_initialized
70
+ _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!")
87
84
 
88
- try:
89
- with open(file_path, "r", encoding="utf-8") as template_file:
90
- content = template_file.read()
91
- return content
92
- except Exception as e:
93
- logging.error(f"Read file '{file_path}' failed")
94
- handle_error(e)
85
+ _langfuse_initialized = True
86
+ return _langfuse
87
+
88
+ langfuse: Langfuse = Langfuse if _langfuse_initialized else setup_langfuse()
95
89
 
96
- def setup_xga_env() -> None:
97
- from dotenv import load_dotenv
98
- load_dotenv()
99
- setup_logging()
100
- setup_langfuse()
101
90
 
102
91
  if __name__ == "__main__":
103
92
  try:
104
- setup_xga_env()
105
93
  trace_id = langfuse.create_trace_id()
106
94
  print(f"trace_id={trace_id}")
107
95
  except Exception as e:
xgae/utils/utils.py ADDED
@@ -0,0 +1,42 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import datetime
5
+
6
+ from typing import Any, Dict
7
+
8
+ def handle_error(e: Exception) -> None:
9
+ import traceback
10
+
11
+ logging.error("An error occurred: %s", str(e))
12
+ logging.error("Traceback details:\n%s", traceback.format_exc())
13
+ raise (e) from e
14
+
15
+ def read_file(file_path: str) -> str:
16
+ if not os.path.exists(file_path):
17
+ logging.error(f"File '{file_path}' not found")
18
+ raise Exception(f"File '{file_path}' not found")
19
+
20
+ try:
21
+ with open(file_path, "r", encoding="utf-8") as template_file:
22
+ content = template_file.read()
23
+ return content
24
+ except Exception as e:
25
+ logging.error(f"Read file '{file_path}' failed")
26
+ handle_error(e)
27
+
28
+ def format_file_with_args(file_content:str, args: Dict[str, Any])-> str:
29
+ from io import StringIO
30
+
31
+ formated = file_content
32
+ original_stdout = sys.stdout
33
+ buffer = StringIO()
34
+ sys.stdout = buffer
35
+ try:
36
+ code = f"print(f\"\"\"{file_content}\"\"\")"
37
+ exec(code, args)
38
+ formated = buffer.getvalue()
39
+ finally:
40
+ sys.stdout = original_stdout
41
+
42
+ return formated
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog>=6.9.0
@@ -0,0 +1,14 @@
1
+ xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ xgae/engine/xga_base.py,sha256=b9FnBbCuO4lCAIBBhnedtRk49Dl3M6cBn5h_Wkzw4o0,1345
3
+ xgae/engine/xga_engine.py,sha256=kci7pvF8NwAYJ3Y6DS0JKLkPEQERZwcwRzjcAFQx_ec,4169
4
+ xgae/engine/xga_mcp_tool_box.py,sha256=coyQy7tj2YTBpOc0JKmVBJO8ItO4Z6s1pdbucHd85qg,9762
5
+ xgae/engine/xga_prompt_builder.py,sha256=RuTvQCNufqxDwVvSOPXR0qxAc42cG7NuIaUy9amu66A,4351
6
+ xgae/engine/responser/xga_non_stream_responser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ xgae/engine/responser/xga_responser_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ xgae/engine/responser/xga_stream_reponser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ xgae/utils/llm_client.py,sha256=UMMK84psLsx36-Nn6Q8X1hl9wd-OdaS9ZhxbRjwNCr0,12149
10
+ xgae/utils/setup_env.py,sha256=vY2wnq5KMXQ2dx2wAPeTpudp7b-fvgzenkwHpgd8vuI,2951
11
+ xgae/utils/utils.py,sha256=cCYmWjKFksZ8BRD1YYnaM_jTLVHAg1ibEdjsczEUO6k,1134
12
+ xgae-0.1.3.dist-info/METADATA,sha256=t4sr4k9pbVAcicIRjoqt327eWtSC0uAeo2pDB5LPjs0,309
13
+ xgae-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ xgae-0.1.3.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- xgae/engine/xga_base.py,sha256=ZuOVD5mLnfi8rTnDspY8qfEgwefVTTYgf6eY3B_001s,1213
3
- xgae/engine/xga_engine.py,sha256=3U0F3_ISu5K1CGYhn0RZsIaZkm4OQIrMTIf3N22M7bE,3013
4
- xgae/engine/xga_mcp_tool_box.py,sha256=3L48c-Wl8QghbudUsZdYemOzEHoOGF0QC-sAh0H1PVI,9047
5
- xgae/engine/xga_prompt_builder.py,sha256=wPCB6g0QNpKDuvWs5Ix_Z8-OoCFjxqApodb-g-qZrbM,1503
6
- xgae/engine/responser/xga_non_stream_responser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- xgae/engine/responser/xga_responser_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- xgae/engine/responser/xga_stream_reponser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- xgae/utils/llm_client.py,sha256=bsdWLTXRDDpiuEn72a5BuT4FYomE1LjY-thQHbTQ_Fg,12167
10
- xgae/utils/setup_env.py,sha256=nJHllWuM9kLs_mJg_-j3Lhj8FHtyFjK4CIoDY_SHOxk,3253
11
- xgae-0.1.1.dist-info/METADATA,sha256=_j2ZiXzJZa-sw_fk7P0sHkKiyYus_ncxoL9YoZEv4iQ,309
12
- xgae-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- xgae-0.1.1.dist-info/RECORD,,
File without changes