aiecs 1.7.17__py3-none-any.whl → 1.8.4__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 aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +1 -1
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +5 -1
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +7 -5
- aiecs/config/config.py +3 -0
- aiecs/domain/agent/hybrid_agent.py +93 -12
- aiecs/domain/agent/knowledge_aware_agent.py +3 -2
- aiecs/domain/agent/llm_agent.py +2 -0
- aiecs/llm/callbacks/custom_callbacks.py +9 -4
- aiecs/llm/client_factory.py +14 -6
- aiecs/llm/clients/base_client.py +45 -4
- aiecs/llm/clients/googleai_client.py +105 -4
- aiecs/llm/clients/openai_client.py +12 -0
- aiecs/llm/clients/openai_compatible_mixin.py +42 -2
- aiecs/llm/clients/openrouter_client.py +272 -0
- aiecs/llm/clients/vertex_client.py +79 -5
- aiecs/llm/clients/xai_client.py +41 -3
- aiecs/llm/protocols.py +19 -1
- aiecs/llm/utils/image_utils.py +179 -0
- aiecs/main.py +2 -2
- aiecs/tools/task_tools/scraper_tool.py +39 -2
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/METADATA +4 -2
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/RECORD +26 -24
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/WHEEL +0 -0
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/entry_points.txt +0 -0
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.7.17.dist-info → aiecs-1.8.4.dist-info}/top_level.txt +0 -0
aiecs/llm/clients/xai_client.py
CHANGED
|
@@ -87,6 +87,7 @@ class XAIClient(BaseLLMClient, OpenAICompatibleFunctionCallingMixin):
|
|
|
87
87
|
model: Optional[str] = None,
|
|
88
88
|
temperature: float = 0.7,
|
|
89
89
|
max_tokens: Optional[int] = None,
|
|
90
|
+
context: Optional[Dict[str, Any]] = None,
|
|
90
91
|
functions: Optional[List[Dict[str, Any]]] = None,
|
|
91
92
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
92
93
|
tool_choice: Optional[Any] = None,
|
|
@@ -94,8 +95,27 @@ class XAIClient(BaseLLMClient, OpenAICompatibleFunctionCallingMixin):
|
|
|
94
95
|
) -> LLMResponse:
|
|
95
96
|
"""
|
|
96
97
|
Generate text using xAI API via OpenAI library (supports all Grok models).
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
xAI API is OpenAI-compatible, so it supports Function Calling.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
messages: List of conversation messages
|
|
103
|
+
model: Model name (optional, uses default if not provided)
|
|
104
|
+
temperature: Sampling temperature (0.0 to 1.0)
|
|
105
|
+
max_tokens: Maximum tokens to generate
|
|
106
|
+
context: Optional context dictionary containing metadata such as:
|
|
107
|
+
- user_id: User identifier for tracking/billing
|
|
108
|
+
- tenant_id: Tenant identifier for multi-tenant setups
|
|
109
|
+
- request_id: Request identifier for tracing
|
|
110
|
+
- session_id: Session identifier
|
|
111
|
+
- Any other custom metadata for observability or middleware
|
|
112
|
+
functions: List of function schemas (legacy format)
|
|
113
|
+
tools: List of tool schemas (new format, recommended)
|
|
114
|
+
tool_choice: Tool choice strategy
|
|
115
|
+
**kwargs: Additional provider-specific parameters
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
LLMResponse with generated text and metadata
|
|
99
119
|
"""
|
|
100
120
|
# Check API key availability
|
|
101
121
|
api_key = self._get_api_key()
|
|
@@ -144,6 +164,7 @@ class XAIClient(BaseLLMClient, OpenAICompatibleFunctionCallingMixin):
|
|
|
144
164
|
model: Optional[str] = None,
|
|
145
165
|
temperature: float = 0.7,
|
|
146
166
|
max_tokens: Optional[int] = None,
|
|
167
|
+
context: Optional[Dict[str, Any]] = None,
|
|
147
168
|
functions: Optional[List[Dict[str, Any]]] = None,
|
|
148
169
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
149
170
|
tool_choice: Optional[Any] = None,
|
|
@@ -152,11 +173,28 @@ class XAIClient(BaseLLMClient, OpenAICompatibleFunctionCallingMixin):
|
|
|
152
173
|
) -> AsyncGenerator[Any, None]:
|
|
153
174
|
"""
|
|
154
175
|
Stream text using xAI API via OpenAI library (supports all Grok models).
|
|
155
|
-
|
|
176
|
+
|
|
156
177
|
xAI API is OpenAI-compatible, so it supports Function Calling.
|
|
157
|
-
|
|
178
|
+
|
|
158
179
|
Args:
|
|
180
|
+
messages: List of conversation messages
|
|
181
|
+
model: Model name (optional, uses default if not provided)
|
|
182
|
+
temperature: Sampling temperature (0.0 to 1.0)
|
|
183
|
+
max_tokens: Maximum tokens to generate
|
|
184
|
+
context: Optional context dictionary containing metadata such as:
|
|
185
|
+
- user_id: User identifier for tracking/billing
|
|
186
|
+
- tenant_id: Tenant identifier for multi-tenant setups
|
|
187
|
+
- request_id: Request identifier for tracing
|
|
188
|
+
- session_id: Session identifier
|
|
189
|
+
- Any other custom metadata for observability or middleware
|
|
190
|
+
functions: List of function schemas (legacy format)
|
|
191
|
+
tools: List of tool schemas (new format, recommended)
|
|
192
|
+
tool_choice: Tool choice strategy
|
|
159
193
|
return_chunks: If True, returns StreamChunk objects with tool_calls info; if False, returns str tokens only
|
|
194
|
+
**kwargs: Additional provider-specific parameters
|
|
195
|
+
|
|
196
|
+
Yields:
|
|
197
|
+
str or StreamChunk: Text tokens or StreamChunk objects
|
|
160
198
|
"""
|
|
161
199
|
# Check API key availability
|
|
162
200
|
api_key = self._get_api_key()
|
aiecs/llm/protocols.py
CHANGED
|
@@ -4,7 +4,7 @@ LLM Client Protocols
|
|
|
4
4
|
Defines Protocol interfaces for LLM clients to enable duck typing and flexible integration.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Protocol, List, Optional, AsyncGenerator, runtime_checkable
|
|
7
|
+
from typing import Protocol, List, Optional, AsyncGenerator, runtime_checkable, Dict, Any
|
|
8
8
|
from aiecs.llm.clients.base_client import LLMMessage, LLMResponse
|
|
9
9
|
|
|
10
10
|
|
|
@@ -31,9 +31,12 @@ class LLMClientProtocol(Protocol):
|
|
|
31
31
|
model: Optional[str] = None,
|
|
32
32
|
temperature: float = 0.7,
|
|
33
33
|
max_tokens: Optional[int] = None,
|
|
34
|
+
context: Optional[Dict[str, Any]] = None,
|
|
34
35
|
**kwargs
|
|
35
36
|
) -> LLMResponse:
|
|
36
37
|
# Custom implementation
|
|
38
|
+
# Use context for tracking, billing, observability, etc.
|
|
39
|
+
user_id = context.get("user_id") if context else None
|
|
37
40
|
pass
|
|
38
41
|
|
|
39
42
|
async def stream_text(
|
|
@@ -42,6 +45,7 @@ class LLMClientProtocol(Protocol):
|
|
|
42
45
|
model: Optional[str] = None,
|
|
43
46
|
temperature: float = 0.7,
|
|
44
47
|
max_tokens: Optional[int] = None,
|
|
48
|
+
context: Optional[Dict[str, Any]] = None,
|
|
45
49
|
**kwargs
|
|
46
50
|
) -> AsyncGenerator[str, None]:
|
|
47
51
|
# Custom implementation
|
|
@@ -67,6 +71,7 @@ class LLMClientProtocol(Protocol):
|
|
|
67
71
|
model: Optional[str] = None,
|
|
68
72
|
temperature: float = 0.7,
|
|
69
73
|
max_tokens: Optional[int] = None,
|
|
74
|
+
context: Optional[Dict[str, Any]] = None,
|
|
70
75
|
**kwargs,
|
|
71
76
|
) -> LLMResponse:
|
|
72
77
|
"""
|
|
@@ -77,6 +82,12 @@ class LLMClientProtocol(Protocol):
|
|
|
77
82
|
model: Model name (optional, uses default if not provided)
|
|
78
83
|
temperature: Sampling temperature (0.0 to 1.0)
|
|
79
84
|
max_tokens: Maximum tokens to generate
|
|
85
|
+
context: Optional context dictionary containing metadata such as:
|
|
86
|
+
- user_id: User identifier for tracking/billing
|
|
87
|
+
- tenant_id: Tenant identifier for multi-tenant setups
|
|
88
|
+
- request_id: Request identifier for tracing
|
|
89
|
+
- session_id: Session identifier
|
|
90
|
+
- Any other custom metadata for observability or middleware
|
|
80
91
|
**kwargs: Additional provider-specific parameters
|
|
81
92
|
|
|
82
93
|
Returns:
|
|
@@ -90,6 +101,7 @@ class LLMClientProtocol(Protocol):
|
|
|
90
101
|
model: Optional[str] = None,
|
|
91
102
|
temperature: float = 0.7,
|
|
92
103
|
max_tokens: Optional[int] = None,
|
|
104
|
+
context: Optional[Dict[str, Any]] = None,
|
|
93
105
|
**kwargs,
|
|
94
106
|
) -> AsyncGenerator[str, None]:
|
|
95
107
|
"""
|
|
@@ -100,6 +112,12 @@ class LLMClientProtocol(Protocol):
|
|
|
100
112
|
model: Model name (optional, uses default if not provided)
|
|
101
113
|
temperature: Sampling temperature (0.0 to 1.0)
|
|
102
114
|
max_tokens: Maximum tokens to generate
|
|
115
|
+
context: Optional context dictionary containing metadata such as:
|
|
116
|
+
- user_id: User identifier for tracking/billing
|
|
117
|
+
- tenant_id: Tenant identifier for multi-tenant setups
|
|
118
|
+
- request_id: Request identifier for tracing
|
|
119
|
+
- session_id: Session identifier
|
|
120
|
+
- Any other custom metadata for observability or middleware
|
|
103
121
|
**kwargs: Additional provider-specific parameters
|
|
104
122
|
|
|
105
123
|
Yields:
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Image processing utilities for LLM vision support.
|
|
3
|
+
|
|
4
|
+
This module provides functions to handle images in various formats:
|
|
5
|
+
- Image URLs
|
|
6
|
+
- Base64-encoded images
|
|
7
|
+
- Local file paths
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import base64
|
|
12
|
+
import mimetypes
|
|
13
|
+
from typing import Union, Optional, Dict, Any
|
|
14
|
+
from urllib.parse import urlparse
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImageContent:
|
|
21
|
+
"""Represents image content for LLM messages."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
source: str,
|
|
26
|
+
mime_type: Optional[str] = None,
|
|
27
|
+
detail: Optional[str] = None,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Initialize image content.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
source: Image source - can be URL, base64 data URI, or file path
|
|
34
|
+
mime_type: MIME type (e.g., 'image/jpeg', 'image/png'). Auto-detected if not provided.
|
|
35
|
+
detail: Detail level for OpenAI API ('low', 'high', 'auto'). Defaults to 'auto'.
|
|
36
|
+
"""
|
|
37
|
+
self.source = source
|
|
38
|
+
self.mime_type = mime_type or self._detect_mime_type(source)
|
|
39
|
+
self.detail = detail or "auto"
|
|
40
|
+
|
|
41
|
+
def _detect_mime_type(self, source: str) -> str:
|
|
42
|
+
"""Detect MIME type from source."""
|
|
43
|
+
# Check if it's a data URI
|
|
44
|
+
if source.startswith("data:"):
|
|
45
|
+
header = source.split(",")[0]
|
|
46
|
+
mime_type = header.split(":")[1].split(";")[0]
|
|
47
|
+
return mime_type
|
|
48
|
+
|
|
49
|
+
# Check if it's a URL
|
|
50
|
+
if source.startswith(("http://", "https://")):
|
|
51
|
+
parsed = urlparse(source)
|
|
52
|
+
mime_type, _ = mimetypes.guess_type(parsed.path)
|
|
53
|
+
if mime_type and mime_type.startswith("image/"):
|
|
54
|
+
return mime_type
|
|
55
|
+
|
|
56
|
+
# Check if it's a file path
|
|
57
|
+
if os.path.exists(source):
|
|
58
|
+
mime_type, _ = mimetypes.guess_type(source)
|
|
59
|
+
if mime_type and mime_type.startswith("image/"):
|
|
60
|
+
return mime_type
|
|
61
|
+
|
|
62
|
+
# Default to jpeg if cannot detect
|
|
63
|
+
logger.warning(f"Could not detect MIME type for {source}, defaulting to image/jpeg")
|
|
64
|
+
return "image/jpeg"
|
|
65
|
+
|
|
66
|
+
def is_url(self) -> bool:
|
|
67
|
+
"""Check if source is a URL."""
|
|
68
|
+
return self.source.startswith(("http://", "https://"))
|
|
69
|
+
|
|
70
|
+
def is_base64(self) -> bool:
|
|
71
|
+
"""Check if source is a base64 data URI."""
|
|
72
|
+
return self.source.startswith("data:")
|
|
73
|
+
|
|
74
|
+
def is_file_path(self) -> bool:
|
|
75
|
+
"""Check if source is a local file path."""
|
|
76
|
+
return os.path.exists(self.source) and not self.is_url() and not self.is_base64()
|
|
77
|
+
|
|
78
|
+
def get_base64_data(self) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Get base64-encoded image data.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Base64 string without data URI prefix
|
|
84
|
+
"""
|
|
85
|
+
if self.is_base64():
|
|
86
|
+
# Extract base64 data from data URI
|
|
87
|
+
return self.source.split(",", 1)[1]
|
|
88
|
+
elif self.is_file_path():
|
|
89
|
+
# Read file and encode to base64
|
|
90
|
+
with open(self.source, "rb") as f:
|
|
91
|
+
return base64.b64encode(f.read()).decode("utf-8")
|
|
92
|
+
else:
|
|
93
|
+
raise ValueError(f"Cannot get base64 data from URL: {self.source}. Use URL directly or download first.")
|
|
94
|
+
|
|
95
|
+
def get_url(self) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Get image URL.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
URL string
|
|
101
|
+
"""
|
|
102
|
+
if self.is_url():
|
|
103
|
+
return self.source
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError(f"Source is not a URL: {self.source}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def parse_image_source(source: Union[str, Dict[str, Any]]) -> ImageContent:
|
|
109
|
+
"""
|
|
110
|
+
Parse image source into ImageContent object.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
source: Can be:
|
|
114
|
+
- String URL (http://... or https://...)
|
|
115
|
+
- String base64 data URI (data:image/...;base64,...)
|
|
116
|
+
- String file path
|
|
117
|
+
- Dict with 'url', 'data', or 'path' key
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
ImageContent object
|
|
121
|
+
"""
|
|
122
|
+
if isinstance(source, dict):
|
|
123
|
+
# Handle dict format
|
|
124
|
+
if "url" in source:
|
|
125
|
+
return ImageContent(
|
|
126
|
+
source=source["url"],
|
|
127
|
+
mime_type=source.get("mime_type"),
|
|
128
|
+
detail=source.get("detail"),
|
|
129
|
+
)
|
|
130
|
+
elif "data" in source:
|
|
131
|
+
# Base64 data
|
|
132
|
+
mime_type = source.get("mime_type", "image/jpeg")
|
|
133
|
+
data = source["data"]
|
|
134
|
+
if not data.startswith("data:"):
|
|
135
|
+
data = f"data:{mime_type};base64,{data}"
|
|
136
|
+
return ImageContent(
|
|
137
|
+
source=data,
|
|
138
|
+
mime_type=mime_type,
|
|
139
|
+
detail=source.get("detail"),
|
|
140
|
+
)
|
|
141
|
+
elif "path" in source:
|
|
142
|
+
return ImageContent(
|
|
143
|
+
source=source["path"],
|
|
144
|
+
mime_type=source.get("mime_type"),
|
|
145
|
+
detail=source.get("detail"),
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
raise ValueError(f"Invalid image dict format: {source}")
|
|
149
|
+
elif isinstance(source, str):
|
|
150
|
+
return ImageContent(source=source)
|
|
151
|
+
else:
|
|
152
|
+
raise TypeError(f"Image source must be str or dict, got {type(source)}")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def validate_image_source(source: str) -> bool:
|
|
156
|
+
"""
|
|
157
|
+
Validate that image source is accessible.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
source: Image source (URL, base64, or file path)
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
True if source is valid and accessible
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
img = ImageContent(source)
|
|
167
|
+
if img.is_file_path():
|
|
168
|
+
return os.path.exists(source) and os.path.isfile(source)
|
|
169
|
+
elif img.is_url():
|
|
170
|
+
# URL validation - just check format, not accessibility
|
|
171
|
+
parsed = urlparse(source)
|
|
172
|
+
return bool(parsed.scheme and parsed.netloc)
|
|
173
|
+
elif img.is_base64():
|
|
174
|
+
# Base64 validation - check format
|
|
175
|
+
parts = source.split(",", 1)
|
|
176
|
+
return len(parts) == 2 and parts[0].startswith("data:image/")
|
|
177
|
+
return False
|
|
178
|
+
except Exception:
|
|
179
|
+
return False
|
aiecs/main.py
CHANGED
|
@@ -142,7 +142,7 @@ async def lifespan(app: FastAPI):
|
|
|
142
142
|
app = FastAPI(
|
|
143
143
|
title="AIECS - AI Execute Services",
|
|
144
144
|
description="Middleware service for AI-powered task execution and tool orchestration",
|
|
145
|
-
version="1.
|
|
145
|
+
version="1.8.4",
|
|
146
146
|
lifespan=lifespan,
|
|
147
147
|
)
|
|
148
148
|
|
|
@@ -164,7 +164,7 @@ socket_app = socketio.ASGIApp(sio, other_asgi_app=app)
|
|
|
164
164
|
@app.get("/health")
|
|
165
165
|
async def health_check():
|
|
166
166
|
"""Health check endpoint"""
|
|
167
|
-
return {"status": "healthy", "service": "aiecs", "version": "1.
|
|
167
|
+
return {"status": "healthy", "service": "aiecs", "version": "1.8.4"}
|
|
168
168
|
|
|
169
169
|
|
|
170
170
|
# Metrics health check endpoint
|
|
@@ -100,7 +100,7 @@ class ScraperTool(BaseTool):
|
|
|
100
100
|
# Configuration schema
|
|
101
101
|
class Config(BaseSettings):
|
|
102
102
|
"""Configuration for the scraper tool
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
Automatically reads from environment variables with SCRAPER_TOOL_ prefix.
|
|
105
105
|
Example: SCRAPER_TOOL_USER_AGENT -> user_agent
|
|
106
106
|
"""
|
|
@@ -126,6 +126,10 @@ class ScraperTool(BaseTool):
|
|
|
126
126
|
default=False,
|
|
127
127
|
description="Whether Playwright is available (auto-detected)",
|
|
128
128
|
)
|
|
129
|
+
use_stealth: bool = Field(
|
|
130
|
+
default=False,
|
|
131
|
+
description="Whether to use stealth mode with Playwright to avoid bot detection",
|
|
132
|
+
)
|
|
129
133
|
|
|
130
134
|
# Schema definitions
|
|
131
135
|
class Get_httpxSchema(BaseModel):
|
|
@@ -508,6 +512,7 @@ class ScraperTool(BaseTool):
|
|
|
508
512
|
headers: Optional[Dict[str, str]] = None,
|
|
509
513
|
output_format: Optional[OutputFormat] = None,
|
|
510
514
|
output_path: Optional[str] = None,
|
|
515
|
+
use_stealth: Optional[bool] = None,
|
|
511
516
|
) -> Dict[str, Any]:
|
|
512
517
|
"""
|
|
513
518
|
Render a web page using a headless browser (Playwright).
|
|
@@ -523,6 +528,7 @@ class ScraperTool(BaseTool):
|
|
|
523
528
|
headers (Optional[Dict[str, str]]): Custom headers.
|
|
524
529
|
output_format (Optional[OutputFormat]): Output format.
|
|
525
530
|
output_path (Optional[str]): Path to save output.
|
|
531
|
+
use_stealth (Optional[bool]): Whether to use stealth mode. If None, uses config value.
|
|
526
532
|
|
|
527
533
|
Returns:
|
|
528
534
|
Dict[str, Any]: Rendered page content {'html': str, 'title': str, 'url': str, 'screenshot': Optional[str]}.
|
|
@@ -541,6 +547,7 @@ class ScraperTool(BaseTool):
|
|
|
541
547
|
scroll_to_bottom,
|
|
542
548
|
screenshot,
|
|
543
549
|
screenshot_path,
|
|
550
|
+
use_stealth if use_stealth is not None else self.config.use_stealth,
|
|
544
551
|
)
|
|
545
552
|
else:
|
|
546
553
|
raise RenderingError(f"Unsupported rendering engine: {engine}. Only PLAYWRIGHT is supported.")
|
|
@@ -559,8 +566,22 @@ class ScraperTool(BaseTool):
|
|
|
559
566
|
scroll_to_bottom: bool,
|
|
560
567
|
screenshot: bool,
|
|
561
568
|
screenshot_path: Optional[str],
|
|
569
|
+
use_stealth: bool = False,
|
|
562
570
|
) -> Dict[str, Any]:
|
|
563
|
-
"""Render a web page using Playwright with async API.
|
|
571
|
+
"""Render a web page using Playwright with async API.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
url (str): URL to render.
|
|
575
|
+
wait_time (int): Time to wait for JS execution.
|
|
576
|
+
wait_selector (Optional[str]): CSS selector to wait for.
|
|
577
|
+
scroll_to_bottom (bool): Whether to scroll to the bottom of the page.
|
|
578
|
+
screenshot (bool): Whether to take a screenshot.
|
|
579
|
+
screenshot_path (Optional[str]): Path to save the screenshot.
|
|
580
|
+
use_stealth (bool): Whether to use stealth mode to avoid bot detection.
|
|
581
|
+
|
|
582
|
+
Returns:
|
|
583
|
+
Dict[str, Any]: Rendered page content.
|
|
584
|
+
"""
|
|
564
585
|
from playwright.async_api import async_playwright
|
|
565
586
|
|
|
566
587
|
async with async_playwright() as p:
|
|
@@ -569,6 +590,22 @@ class ScraperTool(BaseTool):
|
|
|
569
590
|
user_agent=self.config.user_agent,
|
|
570
591
|
viewport={"width": 1280, "height": 800},
|
|
571
592
|
)
|
|
593
|
+
|
|
594
|
+
# Apply stealth mode if enabled
|
|
595
|
+
if use_stealth:
|
|
596
|
+
try:
|
|
597
|
+
from playwright_stealth import stealth_async
|
|
598
|
+
await stealth_async(page)
|
|
599
|
+
self.logger.info("Stealth mode enabled for Playwright")
|
|
600
|
+
except ImportError:
|
|
601
|
+
self.logger.warning(
|
|
602
|
+
"playwright-stealth is not installed. "
|
|
603
|
+
"Install it with 'pip install playwright-stealth' to use stealth mode. "
|
|
604
|
+
"Continuing without stealth mode."
|
|
605
|
+
)
|
|
606
|
+
except Exception as e:
|
|
607
|
+
self.logger.warning(f"Failed to apply stealth mode: {str(e)}. Continuing without stealth mode.")
|
|
608
|
+
|
|
572
609
|
try:
|
|
573
610
|
await page.goto(url)
|
|
574
611
|
if wait_selector:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiecs
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.4
|
|
4
4
|
Summary: AI Execute Services - A middleware framework for AI-powered task execution and tool orchestration
|
|
5
5
|
Author-email: AIECS Team <iretbl@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -48,7 +48,7 @@ Requires-Dist: langgraph<0.6.0,>=0.5.3
|
|
|
48
48
|
Requires-Dist: weasel==0.4.1
|
|
49
49
|
Requires-Dist: spacy<4.0.0,>=3.8.7
|
|
50
50
|
Requires-Dist: rake-nltk<2.0.0,>=1.0.6
|
|
51
|
-
Requires-Dist: numpy<3.0.0,>=2.
|
|
51
|
+
Requires-Dist: numpy<3.0.0,>=2.4.1
|
|
52
52
|
Requires-Dist: pandas<3.0.0,>=2.2.3
|
|
53
53
|
Requires-Dist: scipy<2.0.0,>=1.15.3
|
|
54
54
|
Requires-Dist: scikit-learn<2.0.0,>=1.5.0
|
|
@@ -105,6 +105,8 @@ Requires-Dist: sphinx-rtd-theme<3.0.0,>=2.0.0; extra == "docs"
|
|
|
105
105
|
Requires-Dist: sphinx-autodoc-typehints<3.0.0,>=2.0.0; extra == "docs"
|
|
106
106
|
Requires-Dist: myst-parser<3.0.0,>=2.0.0; extra == "docs"
|
|
107
107
|
Requires-Dist: sphinx-copybutton<1.0.0,>=0.5.0; extra == "docs"
|
|
108
|
+
Provides-Extra: scraper
|
|
109
|
+
Requires-Dist: playwright-stealth<2.0.0,>=1.0.6; extra == "scraper"
|
|
108
110
|
Dynamic: license-file
|
|
109
111
|
|
|
110
112
|
# AIECS - AI Execute Services
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
aiecs/__init__.py,sha256=
|
|
1
|
+
aiecs/__init__.py,sha256=ds9O4SZM1WtfWi6P9v356CyH94U05goAkzlv6XezMGQ,1836
|
|
2
2
|
aiecs/__main__.py,sha256=SUsWL1jOdR3zv6yOi1dAdNH4wvzSVg07Vhlb6I9-bQY,1048
|
|
3
3
|
aiecs/aiecs_client.py,sha256=CjSeiPZ9X0u6xkJcpVRXmCbIm0Wqyx3ejl7o7CaTzUI,18039
|
|
4
|
-
aiecs/main.py,sha256=
|
|
4
|
+
aiecs/main.py,sha256=H7VzC04IaH7P-ucBJ8I8yq5afFptk-q4pvM5WOBCAUA,10720
|
|
5
5
|
aiecs/application/__init__.py,sha256=NkmrUH1DqxJ3vaVC8QwscNdlWqHfC7ZagL4k3nZ_qz4,192
|
|
6
6
|
aiecs/application/executors/__init__.py,sha256=WIl7L9HBsEhNfbNtJdvBvFUJXzESvNZVaiAA6tdtJcs,191
|
|
7
7
|
aiecs/application/executors/operation_executor.py,sha256=WXPLnSYesTrkyVMd5HbOKG705k4eJiEV8200-ZSL0L8,14798
|
|
@@ -18,7 +18,7 @@ aiecs/application/knowledge_graph/builder/structured_pipeline.py,sha256=domjvcxK
|
|
|
18
18
|
aiecs/application/knowledge_graph/builder/text_chunker.py,sha256=jvs4fxPocL_mobLPQqtqsk0Waub11EqUb1PIsLAbxZk,9567
|
|
19
19
|
aiecs/application/knowledge_graph/extractors/__init__.py,sha256=FtCvr-R7CTDcEV4SqtUARoJ5OnULLhw26Ad4NRq8Cmg,705
|
|
20
20
|
aiecs/application/knowledge_graph/extractors/base.py,sha256=XQisuU3UKteHqskqeV0uE_edz7-P03fgJyXDc1YqX1U,3336
|
|
21
|
-
aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py,sha256=
|
|
21
|
+
aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py,sha256=dEV3xrCZ_r5PazT3wR4APaoybcyu1jDBuAkl3rQvO44,15400
|
|
22
22
|
aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py,sha256=4OPvAjy_qj0NaVIA6c50cazu7ILOs_uBXXfcxJHOeM4,11484
|
|
23
23
|
aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py,sha256=2yiGGK3tzjOD-ncMI_5_psKtJyLwpUQ-yuED8q3JZn0,7806
|
|
24
24
|
aiecs/application/knowledge_graph/fusion/__init__.py,sha256=xQ30M8uhOx8-RV8eJ_Mzs2WhNYdu0piYmlncq_MluZ4,2217
|
|
@@ -57,7 +57,7 @@ aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py,sha256
|
|
|
57
57
|
aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py,sha256=-WzYFryZTF_BB_P8NRBZztl_KRtyNtd6Hxz8JIm-Sto,12810
|
|
58
58
|
aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py,sha256=xM9wk_h16TNCffWaqLE8K1LqewBtoTW709KAW3ZoRSw,6429
|
|
59
59
|
aiecs/application/knowledge_graph/retrieval/__init__.py,sha256=pgbycGTCTo4rJQ2dWJrSENlVcf9y3kXpEIazqoJC83k,664
|
|
60
|
-
aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py,sha256=
|
|
60
|
+
aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py,sha256=T5bCIM_ngLti29L6qsWIq9ClZhevvegoA8Zt7Uyyqr0,7003
|
|
61
61
|
aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py,sha256=ya0HbnToPImGvgQyo8gZW_E9V8WX0J1cme8iJW68QZk,18626
|
|
62
62
|
aiecs/application/knowledge_graph/retrieval/strategy_types.py,sha256=HI7yhC1bTD76TtQodk0cQpNO5285IxRkdD_K0p7BlHU,706
|
|
63
63
|
aiecs/application/knowledge_graph/search/__init__.py,sha256=3-c89qUNQq9qcRofVPQanOnKO7G1PEAemJPm98ZRGx4,1471
|
|
@@ -76,7 +76,7 @@ aiecs/common/__init__.py,sha256=6XzlzbA5JU8sFIAEF9-MZTGCbksTqCickXqEITT6cpI,116
|
|
|
76
76
|
aiecs/common/knowledge_graph/__init__.py,sha256=RBvTWuB_PzolXDYBAdqJrFYpQV34VGW0v21t4Bja8Uo,294
|
|
77
77
|
aiecs/common/knowledge_graph/runnable.py,sha256=BLsV9HHfO4lT3_LkFQbf_rUT_iXloZi4D-ztwFqVNuY,15233
|
|
78
78
|
aiecs/config/__init__.py,sha256=aQk0xVquxBZmLTol8wvF4Jv8Yl9G6qyMvA1RL_JE99k,974
|
|
79
|
-
aiecs/config/config.py,sha256=
|
|
79
|
+
aiecs/config/config.py,sha256=FzBVG7ZZ7Nq6shbEEL8Hf9XzNbgr7axl9cZkUi_7GZk,33895
|
|
80
80
|
aiecs/config/graph_config.py,sha256=9-Xrgd1kswfoIep5nzKmVhEz2vPENqtm_l9nZkXLXL0,3993
|
|
81
81
|
aiecs/config/tool_config.py,sha256=iuMBlHlNWoXzcXxVaGuU1BOruh0yQbAZtPTrAqkNPkQ,16301
|
|
82
82
|
aiecs/core/__init__.py,sha256=WnY-iEE0BpdRg26vVSCILvERzcXg6kajS4IiO2-NovI,1471
|
|
@@ -90,10 +90,10 @@ aiecs/domain/agent/__init__.py,sha256=yMDE7r_z8EHGmteWc4E9JoNBVRgeR72VNU7p3c5Obl
|
|
|
90
90
|
aiecs/domain/agent/base_agent.py,sha256=r6jn8Ux9FswRAYXSx8IrsB_SigeCC5VebPLCb7OmJMk,148078
|
|
91
91
|
aiecs/domain/agent/exceptions.py,sha256=axBRJebHP_JpMfjDstOGRxRXsvPXpmZ78Tccv5TxUs0,2951
|
|
92
92
|
aiecs/domain/agent/graph_aware_mixin.py,sha256=1LScf_JuAU67JtuUgoL7DRCQ6WNJA66-U7AXdgEms1Q,18665
|
|
93
|
-
aiecs/domain/agent/hybrid_agent.py,sha256=
|
|
94
|
-
aiecs/domain/agent/knowledge_aware_agent.py,sha256=
|
|
93
|
+
aiecs/domain/agent/hybrid_agent.py,sha256=CAIR6CO9orQmRfjJdxMAEdlaSh0WQoGbr23tlBcP4zg,76325
|
|
94
|
+
aiecs/domain/agent/knowledge_aware_agent.py,sha256=YuIz1nhZUYwH5yGQq0c71d_LFgNNsYMxiVR0g9g2JLA,74096
|
|
95
95
|
aiecs/domain/agent/lifecycle.py,sha256=Mqi0YJYBsTfsD-NC0EZOA1Di889qrnRPzQAehxZO32M,8719
|
|
96
|
-
aiecs/domain/agent/llm_agent.py,sha256=
|
|
96
|
+
aiecs/domain/agent/llm_agent.py,sha256=B0RFbchi8QgJHX-VcypOtCLLqyD2DRFPamljB4L9nKY,24658
|
|
97
97
|
aiecs/domain/agent/models.py,sha256=c7cgW5Oydw86zcfT39CctByWTuZk2FpvWLFjpWjd9pw,36751
|
|
98
98
|
aiecs/domain/agent/observability.py,sha256=BjCzX0f7GJkusEYsYrVZKFXuIoRO9kH3wgVgHb-W5T0,13806
|
|
99
99
|
aiecs/domain/agent/persistence.py,sha256=Qfd8LWR9am4vueJp5eAjjBKuPxlbGsOScSQkUbIwt84,14399
|
|
@@ -195,24 +195,26 @@ aiecs/infrastructure/persistence/database_manager.py,sha256=Oe8Sl-itZBs7bGKcM_50
|
|
|
195
195
|
aiecs/infrastructure/persistence/file_storage.py,sha256=4cZ8AbPMdajfJHUL8ZnuVuLRm7gW74HNQl1fUyfJ90A,26838
|
|
196
196
|
aiecs/infrastructure/persistence/redis_client.py,sha256=oL2C2wmDJC5lFpTEfPle7a1-lvP-3TBxPTIyrskpTlk,7569
|
|
197
197
|
aiecs/llm/__init__.py,sha256=vLn52v1LQgz75iZy6CvTb_vq5rMYbpZFZMFPKxJV-1k,2183
|
|
198
|
-
aiecs/llm/client_factory.py,sha256=
|
|
198
|
+
aiecs/llm/client_factory.py,sha256=gij-1cXXVRRhZZ7X3RRQK5Wj5rD3sNxCDiJiulbwUjU,20389
|
|
199
199
|
aiecs/llm/client_resolver.py,sha256=rRyHYajOk6YpsTRJJW6Y2FwWwpJLe0b77ncaJXX2fwU,5075
|
|
200
|
-
aiecs/llm/protocols.py,sha256=
|
|
200
|
+
aiecs/llm/protocols.py,sha256=PVS-MkS_bmm2FvGXk-NFMqNVbEVPOwNonvSEim3A6N0,5684
|
|
201
201
|
aiecs/llm/callbacks/__init__.py,sha256=u02K5Tvlu-87NXxYzXuxFHuhHAF4OPIje_IvPIF93Lw,191
|
|
202
|
-
aiecs/llm/callbacks/custom_callbacks.py,sha256=
|
|
202
|
+
aiecs/llm/callbacks/custom_callbacks.py,sha256=RFonkZSpgEDgCZ4GhFCLVa7MGBfTddwMGnmmcid-otc,10164
|
|
203
203
|
aiecs/llm/clients/__init__.py,sha256=GaUkYc0mnKrsBceGrjDZ3K6LZUVxGxHqxjB9zSxWRq8,822
|
|
204
|
-
aiecs/llm/clients/base_client.py,sha256=
|
|
204
|
+
aiecs/llm/clients/base_client.py,sha256=X_wILA3bxftatMQ2FBAzT922-9SwaHRfBpJvCGwi-ig,13103
|
|
205
205
|
aiecs/llm/clients/google_function_calling_mixin.py,sha256=od9-kgnhKtqJc_b-M0Xi2VGAlu8QwGR5MyXt0XpoxdM,16636
|
|
206
|
-
aiecs/llm/clients/googleai_client.py,sha256
|
|
207
|
-
aiecs/llm/clients/openai_client.py,sha256=
|
|
208
|
-
aiecs/llm/clients/openai_compatible_mixin.py,sha256=
|
|
209
|
-
aiecs/llm/clients/
|
|
210
|
-
aiecs/llm/clients/
|
|
206
|
+
aiecs/llm/clients/googleai_client.py,sha256=-DM2V8RaoXLoAG7hH0j56LnCvWrhI3pRv56VaQXKb3Y,17107
|
|
207
|
+
aiecs/llm/clients/openai_client.py,sha256=LacvKQlxEcoN7TWuRvBNTz4mM2JhVBdz8lk9ZNB3LL4,6328
|
|
208
|
+
aiecs/llm/clients/openai_compatible_mixin.py,sha256=51tvJQCi-GgDJNN-V_1iTe-N1xFqmd0A5kR1w-udcJc,16224
|
|
209
|
+
aiecs/llm/clients/openrouter_client.py,sha256=bN4aaicZHYv7UHoT4L7_7HhZcNf854DAbaSp2A67-Gg,10357
|
|
210
|
+
aiecs/llm/clients/vertex_client.py,sha256=9yRDvrR9KIt5_K0u9xk-ZwrxUNDmzpePcvEwf5__yNs,58147
|
|
211
|
+
aiecs/llm/clients/xai_client.py,sha256=e76ZuwnLZ4t1zjGtnnyTAm_8lE2VJyaqG1Pgh6uK74E,9162
|
|
211
212
|
aiecs/llm/config/__init__.py,sha256=LCPZXrV4-zUMh6IS32WaUBMT43pSF8dqbhqH3xBcwJU,1107
|
|
212
213
|
aiecs/llm/config/config_loader.py,sha256=Jhd6y4FyMQO73Mwv45Qq--pmPh8XRAzbVxi1xqAgWF8,8653
|
|
213
214
|
aiecs/llm/config/config_validator.py,sha256=6pXxn7O8wSdoWf3zJdmCbufqR-ojWlDKaYrkly55D9Y,7235
|
|
214
215
|
aiecs/llm/config/model_config.py,sha256=ZlUmjlhS_U_BdRe3Y3Zk5M1029ctXHBSwdYqF07H_z8,5687
|
|
215
216
|
aiecs/llm/utils/__init__.py,sha256=zKlDyNjvJsv5URW0B1g4C9cimuB7jmKkcfxkLsTAOVY,239
|
|
217
|
+
aiecs/llm/utils/image_utils.py,sha256=yBuc1vH6YS4DIb7R3zL7ofUItgJzeOJw5S_CC8v70-Y,5744
|
|
216
218
|
aiecs/llm/utils/validate_config.py,sha256=lfi30kPSWoFqDfZvbZTjQfBWMvupBpDh_CzHfDKU3Sw,2959
|
|
217
219
|
aiecs/scripts/__init__.py,sha256=cVxQ5iqym520eDQSpV7B6hWolndCLMOVdvhC_D280PE,66
|
|
218
220
|
aiecs/scripts/aid/VERSION_MANAGEMENT.md,sha256=e0ZL0-ou9R6TW1U4QXNDNfiXVjK6m6SDub9KuJzu98Y,3162
|
|
@@ -315,7 +317,7 @@ aiecs/tools/task_tools/office_tool.py,sha256=0Nw5ppP9sXnnk65yVt9BQYy3Nil8Td33N6u
|
|
|
315
317
|
aiecs/tools/task_tools/pandas_tool.py,sha256=5MHoVz24p178xBZPhIo_dsiej4645jmZvEiy_i0e1mo,41832
|
|
316
318
|
aiecs/tools/task_tools/report_tool.py,sha256=8vIoFLfQvmOjUhFoht2tyZ5Wq7DqsPfXvx0lzORnxs0,34934
|
|
317
319
|
aiecs/tools/task_tools/research_tool.py,sha256=comD38XNHMwrQyGYuP5KWVeobF4inN2o3JCPsox6ixE,19198
|
|
318
|
-
aiecs/tools/task_tools/scraper_tool.py,sha256=
|
|
320
|
+
aiecs/tools/task_tools/scraper_tool.py,sha256=LXEp7IOBaiyfsrUyLzk8MZpK3yXKqssmsxGI7AVQgAk,31281
|
|
319
321
|
aiecs/tools/task_tools/stats_tool.py,sha256=f_PrPgOOiJXpW-aA9_hXhyhpW7D2-QkPjnYF0Duh7tg,33563
|
|
320
322
|
aiecs/tools/tool_executor/__init__.py,sha256=sR2ss7Ep7OKSu0d0566rd14DDkH3tkYmbMLxpEs6kO8,783
|
|
321
323
|
aiecs/tools/tool_executor/tool_executor.py,sha256=WrQMwiiVi-GM8o41zIcT8hUZzvcOXdAhOenBPe998sw,30387
|
|
@@ -329,9 +331,9 @@ aiecs/utils/prompt_loader.py,sha256=iNBNeUSnvbSE1SrllOlBllDdQXtfzq5dO5rjkcFEsSw,
|
|
|
329
331
|
aiecs/utils/token_usage_repository.py,sha256=gFTu-AHawFmScq35T3ib5pTElZrXezu3ijxRRCxW6vc,10497
|
|
330
332
|
aiecs/ws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
331
333
|
aiecs/ws/socket_server.py,sha256=4jJBx93DEOKs2JO-gZAH9R_MfMTVtLLTLtyYnRyCVew,1591
|
|
332
|
-
aiecs-1.
|
|
333
|
-
aiecs-1.
|
|
334
|
-
aiecs-1.
|
|
335
|
-
aiecs-1.
|
|
336
|
-
aiecs-1.
|
|
337
|
-
aiecs-1.
|
|
334
|
+
aiecs-1.8.4.dist-info/licenses/LICENSE,sha256=_1YRaIS0eZu1pv6xfz245UkU0i1Va2B841hv3OWRwqg,12494
|
|
335
|
+
aiecs-1.8.4.dist-info/METADATA,sha256=kb1vMbolQckKE0Mu2hmpsgjB6DZ4fCauGypdgPsETF0,18192
|
|
336
|
+
aiecs-1.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
337
|
+
aiecs-1.8.4.dist-info/entry_points.txt,sha256=t_mIkFuLDmK-2si0thMfgK0WIBg-Ub94V3MchpWMaAs,899
|
|
338
|
+
aiecs-1.8.4.dist-info/top_level.txt,sha256=22IlUlOqh9Ni3jXlQNMNUqzbW8dcxXPeR_EQ-BJVcV8,6
|
|
339
|
+
aiecs-1.8.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|