agent-api-server 2.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.
Files changed (52) hide show
  1. agent_api_server/__init__.py +0 -0
  2. agent_api_server/api/__init__.py +0 -0
  3. agent_api_server/api/v1/__init__.py +0 -0
  4. agent_api_server/api/v1/api.py +25 -0
  5. agent_api_server/api/v1/config.py +57 -0
  6. agent_api_server/api/v1/graph.py +59 -0
  7. agent_api_server/api/v1/schema.py +57 -0
  8. agent_api_server/api/v1/thread.py +563 -0
  9. agent_api_server/cache/__init__.py +0 -0
  10. agent_api_server/cache/redis_cache.py +385 -0
  11. agent_api_server/callback_handler.py +18 -0
  12. agent_api_server/client/css/styles.css +1202 -0
  13. agent_api_server/client/favicon.ico +0 -0
  14. agent_api_server/client/index.html +102 -0
  15. agent_api_server/client/js/app.js +1499 -0
  16. agent_api_server/client/js/index.umd.js +824 -0
  17. agent_api_server/config_center/config_center.py +239 -0
  18. agent_api_server/configs/__init__.py +3 -0
  19. agent_api_server/configs/config.py +163 -0
  20. agent_api_server/dynamic_llm/__init__.py +0 -0
  21. agent_api_server/dynamic_llm/dynamic_llm.py +331 -0
  22. agent_api_server/listener.py +530 -0
  23. agent_api_server/log/__init__.py +0 -0
  24. agent_api_server/log/formatters.py +122 -0
  25. agent_api_server/log/logging.json +50 -0
  26. agent_api_server/mcp_convert/__init__.py +0 -0
  27. agent_api_server/mcp_convert/mcp_convert.py +375 -0
  28. agent_api_server/memeory/__init__.py +0 -0
  29. agent_api_server/memeory/postgres.py +233 -0
  30. agent_api_server/register/__init__.py +0 -0
  31. agent_api_server/register/register.py +65 -0
  32. agent_api_server/service.py +354 -0
  33. agent_api_server/service_hub/service_hub.py +233 -0
  34. agent_api_server/service_hub/service_hub_test.py +700 -0
  35. agent_api_server/shared/__init__.py +0 -0
  36. agent_api_server/shared/ase.py +54 -0
  37. agent_api_server/shared/base_model.py +103 -0
  38. agent_api_server/shared/common.py +110 -0
  39. agent_api_server/shared/decode_token.py +107 -0
  40. agent_api_server/shared/detect_message.py +410 -0
  41. agent_api_server/shared/get_model_info.py +491 -0
  42. agent_api_server/shared/message.py +419 -0
  43. agent_api_server/shared/util_func.py +372 -0
  44. agent_api_server/sso_service/__init__.py +1 -0
  45. agent_api_server/sso_service/sdk/__init__.py +1 -0
  46. agent_api_server/sso_service/sdk/client.py +224 -0
  47. agent_api_server/sso_service/sdk/credential.py +11 -0
  48. agent_api_server/sso_service/sdk/encoding.py +22 -0
  49. agent_api_server/sso_service/sso_service.py +177 -0
  50. agent_api_server-2.1.7.dist-info/METADATA +130 -0
  51. agent_api_server-2.1.7.dist-info/RECORD +52 -0
  52. agent_api_server-2.1.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,239 @@
1
+ import asyncio
2
+ import aiohttp
3
+ import logging
4
+ import os
5
+ import threading
6
+ import time
7
+ from typing import List, Dict
8
+ from urllib.parse import urljoin
9
+
10
+ from agent_api_server.configs import global_config
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class AsyncConfigCenterClient:
16
+ _instance = None
17
+
18
+ def __init__(self):
19
+ self.url = os.getenv("CONFIG_CENTER_ADDRESS") or "http://configcenter:10086"
20
+ if self.url.endswith("/"):
21
+ self.url = self.url.rstrip("/")
22
+ self._agents = []
23
+ self._session = None
24
+ logger.info(f"ConfigCenter Client registered URL is: {self.url}")
25
+
26
+ async def initialize(self):
27
+ if self._session is None or self._session.closed:
28
+ self._session = aiohttp.ClientSession()
29
+ logger.info("ConfigCenter Client initialized")
30
+
31
+ async def close(self):
32
+ if self._session:
33
+ await self._session.close()
34
+ self._session = None
35
+ logger.info("ConfigCenter Client closed")
36
+
37
+ async def ensure_initialized(self):
38
+ if self._session is None or self._session.closed:
39
+ await self.initialize()
40
+
41
+ async def register_to_config_center(self, agents: List[Dict], with_secret: bool) -> None:
42
+ if not agents:
43
+ raise ValueError("Agents list cannot be empty")
44
+
45
+ await self.ensure_initialized()
46
+ self._agents = agents
47
+
48
+ for agent in agents:
49
+ if "agent_name" not in agent:
50
+ raise ValueError(f"Agent is missing 'agent_name' field: {agent}")
51
+ if not agent.get("agent_labels"):
52
+ raise ValueError(f"Agent is missing 'agent_labels' field: {agent}")
53
+
54
+ await self._register_agents(agents, global_config.ENABLE_MCP_SERVER, with_secret)
55
+
56
+ async def _register_agents(self, agents: List[Dict], is_mcp: bool, with_secret: bool) -> None:
57
+ config_endpoint = f"{self.url}/v1/services"
58
+
59
+ main_agent = agents[0]
60
+ if not main_agent.get("agent_labels"):
61
+ raise ValueError("agent_labels is required but empty")
62
+
63
+ service_name = main_agent["agent_labels"][0]
64
+ payload = {
65
+ "name": service_name,
66
+ "serviceInstances": [] if with_secret else [
67
+ {"schema": "postgres"},
68
+ {"schema": "redis"},
69
+ ],
70
+ "routers": {}
71
+ }
72
+
73
+ for agent in agents:
74
+ agent_name = agent["agent_name"]
75
+ payload["routers"][agent_name] = {
76
+ "name": agent_name,
77
+ "address": os.getenv("SERVICE_EXTERNAL_URL")
78
+ }
79
+
80
+ if is_mcp:
81
+ mcp_router_name = f"{service_name}_mcp"
82
+ payload["routers"][mcp_router_name] = {
83
+ "name": mcp_router_name,
84
+ "address": urljoin(os.getenv("MCP_SERVER_EXTERNAL_URL"), "mcp"),
85
+ "isMcpServer": True,
86
+ "mcpServerParams": {
87
+ "isAuth": "true",
88
+ }
89
+ }
90
+
91
+ max_retries = 3
92
+ for attempt in range(max_retries):
93
+ try:
94
+ async with self._session.post(config_endpoint, json=payload, timeout=30) as response:
95
+ response.raise_for_status()
96
+ router_names = list(payload['routers'].keys())
97
+ logger.info(
98
+ f"Service '{service_name}' registered successfully with {len(router_names)} routers: {router_names}")
99
+ return
100
+ except Exception as e:
101
+ logger.error(f"Attempt {attempt + 1}: Failed to register service '{service_name}', "
102
+ f"request url: {config_endpoint}; error {e}")
103
+ if attempt < max_retries - 1:
104
+ await asyncio.sleep(2 ** attempt)
105
+ else:
106
+ logger.error(f"Failed to register service '{service_name}' after 3 attempts")
107
+ raise e
108
+
109
+ def start_periodic_registration(self, agents: List[Dict], with_secret: bool, interval: int = 5):
110
+ if not agents:
111
+ raise ValueError("Agents list cannot be empty")
112
+
113
+ self._agents = agents
114
+
115
+ def registration_loop():
116
+ logger.info(f"🚀 Starting periodic registration thread with interval {interval}s")
117
+
118
+ loop = asyncio.new_event_loop()
119
+ asyncio.set_event_loop(loop)
120
+
121
+ session = aiohttp.ClientSession(loop=loop)
122
+ original_session = self._session
123
+ self._session = session
124
+
125
+ try:
126
+ while True:
127
+ try:
128
+ logger.info(f"🔄 Registration started")
129
+ loop.run_until_complete(self._run_registration_iteration(with_secret))
130
+ logger.info(f"✅ Registration completed successfully")
131
+ time.sleep(interval)
132
+ except Exception as e:
133
+ logger.error(f"❌ Error in registration: {e}")
134
+ time.sleep(5)
135
+ finally:
136
+ loop.run_until_complete(session.close())
137
+ loop.close()
138
+ self._session = original_session
139
+ logger.info("⏹️ Periodic registration thread stopped")
140
+
141
+ thread = threading.Thread(
142
+ target=registration_loop,
143
+ name="config_center_registration",
144
+ daemon=True
145
+ )
146
+ thread.start()
147
+ logger.info("✅ Periodic registration thread started")
148
+
149
+ async def _run_registration_iteration(self, with_secret: bool):
150
+ await self.ensure_initialized()
151
+ await self._register_agents(self._agents, global_config.ENABLE_MCP_SERVER, with_secret)
152
+
153
+ async def database_credential(self) -> dict:
154
+ if not self._agents:
155
+ raise ValueError("No agents registered yet")
156
+
157
+ await self.ensure_initialized()
158
+
159
+ main_agent = self._agents[0]
160
+ if not main_agent.get("agent_labels"):
161
+ raise ValueError("agent_labels is required but empty")
162
+
163
+ service_name = main_agent["agent_labels"][0]
164
+ config_endpoint = urljoin(self.url, f"/v1/{service_name}/serviceintance")
165
+
166
+ async with self._session.get(config_endpoint) as response:
167
+ response.raise_for_status()
168
+ data = await response.json()
169
+
170
+ postgres_url = data.get("postgres", "")
171
+ if postgres_url:
172
+ postgres_url = postgres_url.replace("postgres://", "postgresql://", 1)
173
+
174
+ redis_url = data.get("redis", "")
175
+ return {
176
+ "POSTGRES_URL": postgres_url,
177
+ "REDIS_URL": redis_url
178
+ }
179
+
180
+ async def public_config(self) -> dict:
181
+ await self.ensure_initialized()
182
+ config_endpoint = urljoin(self.url, "/v1/public/configs")
183
+
184
+ async with self._session.get(config_endpoint) as response:
185
+ response.raise_for_status()
186
+ config_data = await response.json()
187
+
188
+ return {
189
+ "cluster": config_data.get("cluster"),
190
+ "datacenter": config_data.get("datacenter"),
191
+ "workspace": config_data.get("workspace"),
192
+ "namespace": config_data.get("namespace")
193
+ }
194
+
195
+ async def service_config(self) -> dict:
196
+ await self.ensure_initialized()
197
+ config_endpoint = urljoin(self.url, "/v1/services")
198
+
199
+ async with self._session.get(config_endpoint) as response:
200
+ response.raise_for_status()
201
+ config_data = await response.json()
202
+
203
+ sso_api_url = config_data.get("SSO", {}).get("routers", {}).get("API", {}).get("address")
204
+ model_management_url = config_data.get("model-management", {}).get("routers", {}).get("API", {}).get("address")
205
+
206
+ return {
207
+ "SSO_URL": sso_api_url,
208
+ "MODEL_MANAGER_SERVICE_URL": model_management_url,
209
+ }
210
+
211
+ async def get_service_internal_url_with_router_path(self, service_name: str, router_path: str) -> str:
212
+ await self.ensure_initialized()
213
+ config_endpoint = urljoin(self.url, "/v1/services")
214
+
215
+ async with self._session.get(config_endpoint) as response:
216
+ response.raise_for_status()
217
+ config_data = await response.json()
218
+
219
+ url = config_data.get(service_name, {}).get("routers", {}).get(router_path, {}).get("address")
220
+
221
+ return url
222
+
223
+ async def get_service_public_url_with_router_path(self, service_name: str, router_path: str) -> str:
224
+ await self.ensure_initialized()
225
+ config_endpoint = urljoin(self.url, "/v1/services")
226
+
227
+ async with self._session.get(config_endpoint) as response:
228
+ response.raise_for_status()
229
+ config_data = await response.json()
230
+
231
+ url = config_data.get(service_name, {}).get("routers", {}).get(router_path, {}).get("publicAddress")
232
+
233
+ return url
234
+
235
+ @staticmethod
236
+ async def get_instance() -> "AsyncConfigCenterClient":
237
+ if AsyncConfigCenterClient._instance is None:
238
+ AsyncConfigCenterClient._instance = AsyncConfigCenterClient()
239
+ return AsyncConfigCenterClient._instance
@@ -0,0 +1,3 @@
1
+ from .config import Config
2
+
3
+ global_config = Config()
@@ -0,0 +1,163 @@
1
+ from pydantic_settings import BaseSettings, SettingsConfigDict
2
+ from pydantic import Field
3
+ from pathlib import Path
4
+ from typing import Optional
5
+ from dotenv import load_dotenv
6
+
7
+
8
+ class SSOConfig(BaseSettings):
9
+ SSO_URL: str = Field(
10
+ default="http://sso/v4.0", description="The URL of SSO service"
11
+ )
12
+ CLIENT_ID: str = Field(default="", description="The client-id of server")
13
+ CLIENT_SECRET: str = Field(default="", description="The client secret of server")
14
+ CLIENT_TOKEN: str = Field(default="", description="The client token of server")
15
+
16
+
17
+ class CacheConfig(BaseSettings):
18
+ REDIS_URL: Optional[str] = Field(
19
+ default="redis://localhost:6379/0",
20
+ description="Redis server URL in format redis://host:port/db"
21
+ )
22
+ MAX_THREADS: int = Field(
23
+ default=1000,
24
+ description="Maximum number of threads for concurrent operations"
25
+ )
26
+
27
+ class PostgresConfig(BaseSettings):
28
+ POSTGRES_URL: Optional[str] = Field(
29
+ default="postgresql://user:password@host:port/dbname",
30
+ description="complete PostgresSQL URL,formater is:postgresql://user:password@host:port/dbname"
31
+ )
32
+
33
+ # 连接池参数
34
+ POSTGRES_POOL_MIN_SIZE: int = Field(
35
+ default=1,
36
+ description="mini connection count for postgres"
37
+ )
38
+
39
+ POSTGRES_POOL_MAX_SIZE: int = Field(
40
+ default=10,
41
+ description="max connection count for postgres"
42
+ )
43
+
44
+ POSTGRES_POOL_TIMEOUT: float = Field(
45
+ default=30.0,
46
+ description="time out for postgres connection"
47
+ )
48
+
49
+ POSTGRES_POOL_RECYCLE: int = Field(
50
+ default=3600,
51
+ description="recycle time for postgres connection"
52
+ )
53
+
54
+ POSTGRES_POOL_MAX_INACTIVE: int = Field(
55
+ default=600,
56
+ description="inactive time for postgres"
57
+ )
58
+
59
+ class LoggingConfig(BaseSettings):
60
+ LOG_LEVEL: str = Field(
61
+ default="INFO", description="Logging level for the application"
62
+ )
63
+
64
+ class ModelManagerServiceConfig(BaseSettings):
65
+ MODEL_MANAGER_SERVICE_URL: str = Field(
66
+ default="https://api-am-ensaas.axa.wise-paas.com.cn", description="The URL of model manager service url"
67
+ )
68
+
69
+ SERVICE_EXTERNAL_URL: str = Field(
70
+ default="http://172.21.84.86:8080", description="The URL of agent"
71
+ )
72
+
73
+ MODEL_MANAGER_REDIS_URL: str = Field(
74
+ default="redis://localhost:6379/0", description="Redis server URL which used by model_manager, format is: redis://host:port/db"
75
+ )
76
+
77
+ MODEL_MANAGER_NATS_URL: str = Field(
78
+ default="", description="NATS server URL which used by model_manager, format is: nats://localhost:4222"
79
+ )
80
+
81
+ class WebServerConfig(BaseSettings):
82
+ SERVER_PORT: int = Field(
83
+ default=8080, description="The port of web server"
84
+ )
85
+
86
+ SERVER_WORKER_AMOUNT: int = Field(
87
+ default=1, description="The amount of web server."
88
+ )
89
+
90
+ AGENT_AUTO_REGISTRATION: bool = Field(
91
+ default=False,
92
+ description="Whether to enable automatic agent registration through API server. "
93
+ "If True, agents will be automatically registered via API. "
94
+ "If False, manual registration is required."
95
+ )
96
+
97
+ ENABLE_MCP_SERVER: bool = Field(
98
+ default=False,
99
+ description="Whether to enable mcp server"
100
+ )
101
+
102
+ MCP_SERVER_PORT: int = Field(
103
+ default=8081, description="The port of mcp server"
104
+ )
105
+
106
+ MCP_SERVER_EXTERNAL_URL: str = Field(
107
+ default="http://172.21.84.86:8081", description="The URL of agent"
108
+ )
109
+
110
+ MCP_GATEWAY_URL: str = Field(
111
+ default="http://172.21.92.186:10105", description="The URL of MCP gateway"
112
+ )
113
+
114
+
115
+ class ConfigCenterConfig(BaseSettings):
116
+ ENABLE_CONFIG_CENTER: bool =Field(
117
+ default=False, description="Whether to enable config center to get service information."
118
+ )
119
+
120
+ CONFIG_CENTER_ADDRESS: str = Field(
121
+ default='', description="The URL of config center."
122
+ )
123
+
124
+ class StorageConfig(BaseSettings):
125
+ STORAGE_ENDPOINT: str = Field(
126
+ default='', description="The URL of storage(like: s3)."
127
+ )
128
+
129
+ ACCESS_KEY: str = Field(
130
+ default='', description="The access key of storage(like: s3)."
131
+ )
132
+
133
+ SECRET_KEY: str = Field(
134
+ default='', description="The secret key of storage(like: s3)."
135
+ )
136
+
137
+
138
+ def find_and_load_dotenv(filename: str = '.env') -> Optional[str]:
139
+ search_paths = [
140
+ Path.cwd() / filename,
141
+ Path(__file__).parent.parent / filename,
142
+ ]
143
+
144
+ for path in search_paths:
145
+ if path.exists():
146
+ load_dotenv(path, override=True)
147
+ return str(path)
148
+
149
+ for parent in Path.cwd().parents:
150
+ candidate = parent / filename
151
+ if candidate.exists():
152
+ load_dotenv(candidate, override=True)
153
+ return str(candidate)
154
+
155
+ return None
156
+
157
+ class Config(PostgresConfig, CacheConfig, LoggingConfig, SSOConfig, WebServerConfig, ModelManagerServiceConfig, ConfigCenterConfig, StorageConfig):
158
+ model_config = SettingsConfigDict(
159
+ env_file=find_and_load_dotenv(),
160
+ env_file_encoding="utf-8",
161
+ extra="ignore",
162
+ )
163
+
File without changes