aixtools 0.3.10__tar.gz → 0.4.0__tar.gz
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 aixtools might be problematic. Click here for more details.
- {aixtools-0.3.10 → aixtools-0.4.0}/PKG-INFO +2 -1
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/_version.py +3 -3
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/app.py +6 -0
- aixtools-0.4.0/aixtools/agents/prompt.py +175 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/config.py +23 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/pyproject.toml +2 -1
- aixtools-0.3.10/aixtools/agents/prompt.py +0 -97
- {aixtools-0.3.10 → aixtools-0.4.0}/README.md +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/config.toml +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/bn.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/en-US.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/gu.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/he-IL.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/hi.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/ja.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/kn.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/ml.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/mr.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/nl.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/ta.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/te.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/.chainlit/translations/zh-CN.json +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/auth_middleware.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/pydantic_ai_adapter/storage.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/remote_agent_connection.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/agent.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/agent_batch.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/nodes_to_md.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/nodes_to_message.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/nodes_to_str.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/agents/print_nodes.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/app.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/auth/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/auth/auth.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/chainlit.md +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/compliance/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/compliance/private_data.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/context.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/db/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/db/database.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/db/vector_db.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/evals/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/evals/__main__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/evals/dataset.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/evals/discovery.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/evals/run_evals.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/google/client.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/app.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/display.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/export.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/filters.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/log_utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/log_view/node_summary.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logfilters/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logfilters/context_filter.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/log_objects.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/logging_config.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/mcp_log_models.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/mcp_logger.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/model_patch_logging.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/logging/open_telemetry.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/client.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/example_client.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/example_server.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/exceptions.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/fast_mcp_log.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/faulty_mcp.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/middleware.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/mcp/server.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/model_patch/model_patch.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/server/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/server/app_mounter.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/server/path.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/server/utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/testing/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/testing/agent_mock.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/testing/aix_test_model.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/testing/mock_tool.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/testing/model_patch_cache.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/tools/doctor/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/tools/doctor/mcp_tool_doctor.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/tools/doctor/tool_doctor.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/tools/doctor/tool_recommendation.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/chainlit/cl_agent_show.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/chainlit/cl_utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/config_util.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/enum_with_description.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/files.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/persisted_dict.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/utils/utils.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/vault/__init__.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools/vault/vault.py +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/aixtools.egg-info/SOURCES.txt +0 -0
- {aixtools-0.3.10 → aixtools-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aixtools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Tools for AI exploration and debugging
|
|
5
5
|
Requires-Python: >=3.11.2
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -26,6 +26,7 @@ Requires-Dist: rich>=14.0.0
|
|
|
26
26
|
Requires-Dist: ruff>=0.11.6
|
|
27
27
|
Requires-Dist: streamlit>=1.44.1
|
|
28
28
|
Requires-Dist: watchdog>=6.0.0
|
|
29
|
+
Requires-Dist: markitdown[docx,pdf,pptx,xls,xlsx]>=0.1.3
|
|
29
30
|
Provides-Extra: test
|
|
30
31
|
Requires-Dist: pyyaml; extra == "test"
|
|
31
32
|
Provides-Extra: feature
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.4.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gc209901eb'
|
|
@@ -32,6 +32,7 @@ from starlette.responses import RedirectResponse
|
|
|
32
32
|
from aixtools.a2a.auth_middleware import AuthMiddleware
|
|
33
33
|
from aixtools.auth.auth import AccessTokenAuthProvider
|
|
34
34
|
from aixtools.context import session_id_var, user_id_var
|
|
35
|
+
from aixtools.utils import config
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class AgentWorkerWithMetadataParser(AgentWorker):
|
|
@@ -140,6 +141,11 @@ def build_a2a_starlette_app(
|
|
|
140
141
|
http_handler=request_handler,
|
|
141
142
|
)
|
|
142
143
|
|
|
144
|
+
if config.SKIP_MCP_AUTHORIZATION:
|
|
145
|
+
auth_provider = None
|
|
146
|
+
elif auth_provider is None:
|
|
147
|
+
auth_provider = AccessTokenAuthProvider()
|
|
148
|
+
|
|
143
149
|
app = server.build()
|
|
144
150
|
if auth_provider:
|
|
145
151
|
app.add_middleware(AuthMiddleware, provider=auth_provider)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Prompt building utilities for Pydantic AI agent, including file handling and context management."""
|
|
2
|
+
|
|
3
|
+
import mimetypes
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path, PurePosixPath
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from markitdown import MarkItDown
|
|
9
|
+
from pydantic_ai import BinaryContent
|
|
10
|
+
|
|
11
|
+
from aixtools.context import SessionIdTuple
|
|
12
|
+
from aixtools.logging.logging_config import get_logger
|
|
13
|
+
from aixtools.server import container_to_host_path
|
|
14
|
+
from aixtools.utils.config import (
|
|
15
|
+
EXTRACTABLE_DOCUMENT_TYPES,
|
|
16
|
+
IMAGE_ATTACHMENT_TYPES,
|
|
17
|
+
MAX_EXTRACTED_TEXT_SIZE,
|
|
18
|
+
MAX_IMAGE_ATTACHMENT_SIZE,
|
|
19
|
+
)
|
|
20
|
+
from aixtools.utils.files import is_text_content
|
|
21
|
+
|
|
22
|
+
logger = get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class FileExtractionResult:
|
|
27
|
+
"""Result of file content extraction.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
content: Extracted file content (str for text/documents, BinaryContent for images, None on failure)
|
|
31
|
+
success: True if file was successfully read or extracted, False on any failure
|
|
32
|
+
error_message: Error description if extraction failed, None otherwise
|
|
33
|
+
was_extracted: True if document extraction via markitdown was used successfully
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
content: str | BinaryContent | None
|
|
37
|
+
success: bool
|
|
38
|
+
error_message: str | None = None
|
|
39
|
+
was_extracted: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def should_be_included_into_context(
|
|
43
|
+
file_content: BinaryContent | str | None,
|
|
44
|
+
*,
|
|
45
|
+
max_image_size_bytes: int = MAX_IMAGE_ATTACHMENT_SIZE,
|
|
46
|
+
max_extracted_text_size_bytes: int = MAX_EXTRACTED_TEXT_SIZE,
|
|
47
|
+
) -> bool:
|
|
48
|
+
"""Check if file content should be included in model context based on type and size limits."""
|
|
49
|
+
if file_content is None:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
# Handle extracted text (strings)
|
|
53
|
+
if isinstance(file_content, str):
|
|
54
|
+
text_size = len(file_content.encode("utf-8"))
|
|
55
|
+
return text_size < max_extracted_text_size_bytes
|
|
56
|
+
|
|
57
|
+
# Handle binary content (images only)
|
|
58
|
+
if isinstance(file_content, BinaryContent):
|
|
59
|
+
if file_content.media_type not in IMAGE_ATTACHMENT_TYPES:
|
|
60
|
+
return False
|
|
61
|
+
image_size = len(file_content.data)
|
|
62
|
+
return image_size < max_image_size_bytes
|
|
63
|
+
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def file_to_binary_content(file_path: str | Path, mime_type: Optional[str] = None) -> FileExtractionResult:
|
|
68
|
+
"""Read file and extract text from documents (PDF, DOCX, XLSX, PPTX) using markitdown."""
|
|
69
|
+
if not mime_type:
|
|
70
|
+
mime_type, _ = mimetypes.guess_type(file_path)
|
|
71
|
+
mime_type = mime_type or "application/octet-stream"
|
|
72
|
+
|
|
73
|
+
# Extract text from supported document types using markitdown
|
|
74
|
+
if mime_type in EXTRACTABLE_DOCUMENT_TYPES:
|
|
75
|
+
try:
|
|
76
|
+
markitdown = MarkItDown()
|
|
77
|
+
result = markitdown.convert(str(file_path))
|
|
78
|
+
return FileExtractionResult(
|
|
79
|
+
content=result.text_content, success=True, error_message=None, was_extracted=True
|
|
80
|
+
)
|
|
81
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
82
|
+
error_msg = f"Extraction failed: {type(e).__name__}: {str(e)}"
|
|
83
|
+
logger.error("Document extraction failed for %s: %s", file_path, error_msg)
|
|
84
|
+
return FileExtractionResult(content=None, success=False, error_message=error_msg)
|
|
85
|
+
|
|
86
|
+
# Read the file data for non-document types
|
|
87
|
+
try:
|
|
88
|
+
with open(file_path, "rb") as f:
|
|
89
|
+
data = f.read()
|
|
90
|
+
|
|
91
|
+
# Return as string if it's text content
|
|
92
|
+
if is_text_content(data, mime_type):
|
|
93
|
+
return FileExtractionResult(content=data.decode("utf-8"), success=True)
|
|
94
|
+
|
|
95
|
+
# Return as binary content for images and other binary files
|
|
96
|
+
return FileExtractionResult(content=BinaryContent(data=data, media_type=mime_type), success=True)
|
|
97
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
98
|
+
error_msg = f"Failed to read file: {type(e).__name__}: {str(e)}"
|
|
99
|
+
logger.error("File reading failed for %s: %s", file_path, error_msg)
|
|
100
|
+
return FileExtractionResult(content=None, success=False, error_message=error_msg)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def truncate_extracted_text(text: str, max_bytes: int = MAX_EXTRACTED_TEXT_SIZE) -> str:
|
|
104
|
+
"""Truncate text to max_bytes with warning prefix."""
|
|
105
|
+
truncated_bytes = text.encode("utf-8")[:max_bytes]
|
|
106
|
+
truncated_text = truncated_bytes.decode("utf-8", errors="ignore")
|
|
107
|
+
|
|
108
|
+
total_chars = len(text)
|
|
109
|
+
truncated_chars = len(truncated_text)
|
|
110
|
+
|
|
111
|
+
return f"[TRUNCATED - showing first {truncated_chars} of {total_chars} characters]\n\n{truncated_text}"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def build_user_input(
|
|
115
|
+
session_tuple: SessionIdTuple,
|
|
116
|
+
user_text: str,
|
|
117
|
+
file_paths: list[Path],
|
|
118
|
+
) -> str | list[str | BinaryContent]:
|
|
119
|
+
"""Build user input for the Pydantic AI agent, including file attachments if provided."""
|
|
120
|
+
if not file_paths:
|
|
121
|
+
return user_text
|
|
122
|
+
|
|
123
|
+
attachment_info_lines = []
|
|
124
|
+
binary_attachments: list[str | BinaryContent] = []
|
|
125
|
+
|
|
126
|
+
for workspace_path in file_paths:
|
|
127
|
+
# Convert Path to PurePosixPath for container_to_host_path
|
|
128
|
+
workspace_posix_path = PurePosixPath(workspace_path)
|
|
129
|
+
host_path = container_to_host_path(workspace_posix_path, ctx=session_tuple)
|
|
130
|
+
|
|
131
|
+
# Handle None return from container_to_host_path
|
|
132
|
+
if host_path is None:
|
|
133
|
+
attachment_info = (
|
|
134
|
+
f"* {workspace_path.name} (path in workspace: {workspace_path}) -- conversion failed: invalid path"
|
|
135
|
+
)
|
|
136
|
+
attachment_info_lines.append(attachment_info)
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
file_size = host_path.stat().st_size
|
|
140
|
+
mime_type, _ = mimetypes.guess_type(host_path)
|
|
141
|
+
mime_type = mime_type or "application/octet-stream"
|
|
142
|
+
|
|
143
|
+
attachment_info = f"* {workspace_path.name} (file_size={file_size} bytes) (path in workspace: {workspace_path})"
|
|
144
|
+
extraction_result = file_to_binary_content(host_path, mime_type)
|
|
145
|
+
|
|
146
|
+
# Handle extraction failure - exclude from attachments
|
|
147
|
+
if not extraction_result.success:
|
|
148
|
+
attachment_info += f" -- extraction failed: {extraction_result.error_message}"
|
|
149
|
+
attachment_info_lines.append(attachment_info)
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
# Handle successful extraction
|
|
153
|
+
if extraction_result.was_extracted:
|
|
154
|
+
attachment_info += " -- extracted as text"
|
|
155
|
+
|
|
156
|
+
# Check if content should be included in context
|
|
157
|
+
if should_be_included_into_context(extraction_result.content) and extraction_result.content is not None:
|
|
158
|
+
binary_attachments.append(extraction_result.content)
|
|
159
|
+
attachment_info += f" -- provided to model context at index {len(binary_attachments) - 1}"
|
|
160
|
+
elif (
|
|
161
|
+
isinstance(extraction_result.content, str) and extraction_result.content and extraction_result.was_extracted
|
|
162
|
+
):
|
|
163
|
+
# Truncate large extracted text and include with warning (only for extracted documents)
|
|
164
|
+
truncated_content = truncate_extracted_text(extraction_result.content)
|
|
165
|
+
binary_attachments.append(truncated_content)
|
|
166
|
+
attachment_info += f" -- truncated and provided to model context at index {len(binary_attachments) - 1}"
|
|
167
|
+
elif extraction_result.content is not None:
|
|
168
|
+
# Content exists but excluded from context (e.g., images too large, non-extracted text)
|
|
169
|
+
attachment_info += " -- too large for context"
|
|
170
|
+
|
|
171
|
+
attachment_info_lines.append(attachment_info)
|
|
172
|
+
|
|
173
|
+
full_prompt = user_text + "\nAttachments:\n" + "\n".join(attachment_info_lines)
|
|
174
|
+
|
|
175
|
+
return [full_prompt] + binary_attachments
|
|
@@ -146,3 +146,26 @@ APP_DEFAULT_SCOPE = get_variable_env("APP_DEFAULT_SCOPE", allow_empty=True)
|
|
|
146
146
|
AUTH_TEST_TOKEN = get_variable_env("AUTH_TEST_TOKEN", allow_empty=True)
|
|
147
147
|
|
|
148
148
|
MCP_TOOLS_MAX_RETRIES = int(get_variable_env("MCP_TOOLS_MAX_RETRIES", default=10))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# File attachment limits and supported types for model context
|
|
152
|
+
# Maximum extracted document text size (5MB default, planned for future use)
|
|
153
|
+
MAX_EXTRACTED_TEXT_SIZE = int(get_variable_env("MAX_EXTRACTED_TEXT_SIZE", default=str(5 * 1024 * 1024)))
|
|
154
|
+
# Maximum image attachment size (2MB default)
|
|
155
|
+
MAX_IMAGE_ATTACHMENT_SIZE = int(get_variable_env("MAX_IMAGE_ATTACHMENT_SIZE", default=str(2 * 1024 * 1024)))
|
|
156
|
+
# Image MIME types that can be attached to model context
|
|
157
|
+
IMAGE_ATTACHMENT_TYPES = {
|
|
158
|
+
"image/png",
|
|
159
|
+
"image/jpeg",
|
|
160
|
+
"image/jpg",
|
|
161
|
+
"image/gif",
|
|
162
|
+
"image/webp",
|
|
163
|
+
}
|
|
164
|
+
# Document MIME types that can be extracted as text
|
|
165
|
+
EXTRACTABLE_DOCUMENT_TYPES = {
|
|
166
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation", # .pptx
|
|
167
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", # .docx
|
|
168
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", # .xlsx
|
|
169
|
+
"application/vnd.ms-excel", # .xls
|
|
170
|
+
"application/pdf", # .pdf
|
|
171
|
+
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"""Prompt building utilities for Pydantic AI agent, including file handling and context management."""
|
|
2
|
-
|
|
3
|
-
import mimetypes
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
from pydantic_ai import BinaryContent
|
|
7
|
-
|
|
8
|
-
from aixtools.context import SessionIdTuple
|
|
9
|
-
from aixtools.server import container_to_host_path
|
|
10
|
-
from aixtools.utils.files import is_text_content
|
|
11
|
-
|
|
12
|
-
CLAUDE_MAX_FILE_SIZE_IN_CONTEXT = 4 * 1024 * 1024 # Claude limit 4.5 MB for PDF files
|
|
13
|
-
CLAUDE_IMAGE_MAX_FILE_SIZE_IN_CONTEXT = (
|
|
14
|
-
5 * 1024 * 1024
|
|
15
|
-
) # Claude limit 5 MB for images, to avoid large image files in context
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def should_be_included_into_context(
|
|
19
|
-
file_content: BinaryContent | str | None,
|
|
20
|
-
file_size: int,
|
|
21
|
-
*,
|
|
22
|
-
max_img_size_bytes: int = CLAUDE_IMAGE_MAX_FILE_SIZE_IN_CONTEXT,
|
|
23
|
-
max_file_size_bytes: int = CLAUDE_MAX_FILE_SIZE_IN_CONTEXT,
|
|
24
|
-
) -> bool:
|
|
25
|
-
"""Decide whether a file content should be included into the model context based on its type and size."""
|
|
26
|
-
if not isinstance(file_content, BinaryContent):
|
|
27
|
-
return False
|
|
28
|
-
|
|
29
|
-
if file_content.media_type.startswith("text/"):
|
|
30
|
-
return False
|
|
31
|
-
|
|
32
|
-
# Exclude archive files as they're not supported by OpenAI models
|
|
33
|
-
archive_types = {
|
|
34
|
-
"application/zip",
|
|
35
|
-
"application/x-tar",
|
|
36
|
-
"application/gzip",
|
|
37
|
-
"application/x-gzip",
|
|
38
|
-
"application/x-rar-compressed",
|
|
39
|
-
"application/x-7z-compressed",
|
|
40
|
-
}
|
|
41
|
-
if file_content.media_type in archive_types:
|
|
42
|
-
return False
|
|
43
|
-
|
|
44
|
-
if file_content.is_image and file_size < max_img_size_bytes:
|
|
45
|
-
return True
|
|
46
|
-
|
|
47
|
-
return file_size < max_file_size_bytes
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def file_to_binary_content(file_path: str | Path, mime_type: str = "") -> str | BinaryContent:
|
|
51
|
-
"""
|
|
52
|
-
Read a file and return its content as either a UTF-8 string (for text files)
|
|
53
|
-
or BinaryContent (for binary files).
|
|
54
|
-
"""
|
|
55
|
-
with open(file_path, "rb") as f:
|
|
56
|
-
data = f.read()
|
|
57
|
-
|
|
58
|
-
if not mime_type:
|
|
59
|
-
mime_type, _ = mimetypes.guess_type(file_path)
|
|
60
|
-
mime_type = mime_type or "application/octet-stream"
|
|
61
|
-
|
|
62
|
-
if is_text_content(data, mime_type):
|
|
63
|
-
return data.decode("utf-8")
|
|
64
|
-
|
|
65
|
-
return BinaryContent(data=data, media_type=mime_type)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def build_user_input(
|
|
69
|
-
session_tuple: SessionIdTuple,
|
|
70
|
-
user_text: str,
|
|
71
|
-
file_paths: list[Path],
|
|
72
|
-
) -> str | list[str | BinaryContent]:
|
|
73
|
-
"""Build user input for the Pydantic AI agent, including file attachments if provided."""
|
|
74
|
-
if not file_paths:
|
|
75
|
-
return user_text
|
|
76
|
-
|
|
77
|
-
attachment_info_lines = []
|
|
78
|
-
binary_attachments = []
|
|
79
|
-
|
|
80
|
-
for workspace_path in file_paths:
|
|
81
|
-
host_path = container_to_host_path(workspace_path, ctx=session_tuple)
|
|
82
|
-
file_size = host_path.stat().st_size
|
|
83
|
-
mime_type, _ = mimetypes.guess_type(host_path)
|
|
84
|
-
mime_type = mime_type or "application/octet-stream"
|
|
85
|
-
|
|
86
|
-
attachment_info = f"* {workspace_path.name} (file_size={file_size} bytes) (path in workspace: {workspace_path})"
|
|
87
|
-
binary_content = file_to_binary_content(host_path, mime_type)
|
|
88
|
-
|
|
89
|
-
if should_be_included_into_context(binary_content, file_size):
|
|
90
|
-
binary_attachments.append(binary_content)
|
|
91
|
-
attachment_info += f" -- provided to model context at index {len(binary_attachments) - 1}"
|
|
92
|
-
|
|
93
|
-
attachment_info_lines.append(attachment_info)
|
|
94
|
-
|
|
95
|
-
full_prompt = user_text + "\nAttachments:\n" + "\n".join(attachment_info_lines)
|
|
96
|
-
|
|
97
|
-
return [full_prompt] + binary_attachments
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aixtools-0.3.10 → aixtools-0.4.0}/aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|