Skip to content

Commit ed7b1d7

Browse files
committed
feat: Add Context management system with data persistence
- Add Context, ContextManager, and ContextSync modules - Implement 15+ new API models for context operations - Add comprehensive documentation and examples - Include extensive test coverage - Update core AGB client with context integration This major update enables persistent data storage and file synchronization across AGB sessions.
1 parent 962b62b commit ed7b1d7

84 files changed

Lines changed: 7579 additions & 1199 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ dmypy.json
130130
.vscode/
131131
.idea/
132132
.cursor/
133+
.cursorrules
133134
CLAUDE.md
134135
.DS_Store
135136

CHANGES.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [0.3.0] - 2025-10-15
4+
5+
### New Features
6+
- **Context Management System**: Complete context management with persistent data storage, file operations, and synchronization
7+
- **Enhanced Logging**: Integrated Loguru for better log formatting and structured logging across all modules
8+
9+
### API Enhancements
10+
- **Context API**: New context endpoints for CRUD operations, file management, and synchronization with region support
11+
- **Session Integration**: Enhanced session management with context synchronization during lifecycle
12+
13+
### Documentation & Testing
14+
- **Context Documentation**: Comprehensive usage guides, API reference, and response type documentation
15+
- **Testing**: Complete test coverage for context operations, file management, and synchronization features
16+
17+
### Bug Fixes
18+
- **API & HTTP**: Fixed parameter validation, async request issues, and data serialization problems
19+
- **Infrastructure**: Updated dependencies, improved CI/CD, and resolved type checking issues
20+
321
## [0.2.2] - 2025-09-23
422
### Bug Fixes
523
- remove oss related code and docs

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ For comprehensive documentation, guides, and examples, visit:
8888

8989
3. Install dependencies:
9090
```bash
91-
pip install -e .[dev,test]
91+
pip install -e ."[dev,test]"
9292
```
9393

9494
## License

agb/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,15 @@
33
from .api.http_client import HTTPClient
44
from .session import Session
55
from .session_params import CreateSessionParams
6+
from .context import Context, ContextService, ContextResult, ContextListResult
7+
from .context_manager import ContextManager, ContextStatusData, ContextInfoResult, ContextSyncResult
8+
from .context_sync import ContextSync, SyncPolicy, UploadPolicy, DownloadPolicy, DeletePolicy, ExtractPolicy, UploadStrategy, DownloadStrategy
69

7-
__all__ = ["AGB", "Session", "CreateSessionParams", "HTTPClient", "Client"]
10+
__all__ = [
11+
"AGB", "Session", "CreateSessionParams", "HTTPClient", "Client",
12+
# Context related exports
13+
"Context", "ContextService", "ContextResult", "ContextListResult",
14+
"ContextManager", "ContextStatusData", "ContextInfoResult", "ContextSyncResult",
15+
"ContextSync", "SyncPolicy", "UploadPolicy", "DownloadPolicy", "DeletePolicy", "ExtractPolicy",
16+
"UploadStrategy", "DownloadStrategy",
17+
]

agb/agb.py

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@
1313
from agb.api.models import (
1414
CreateSessionRequest,
1515
CreateSessionResponse,
16-
ReleaseSessionRequest,
16+
CreateMcpSessionRequestPersistenceDataList,
1717
)
1818
from agb.config import Config, load_config
1919
from agb.model.response import DeleteResult, SessionResult
2020
from agb.session import BaseSession, Session
2121
from agb.session_params import CreateSessionParams
22+
from agb.context import ContextService
23+
from agb.logger import get_logger, log_operation_start, log_operation_success, log_warning
24+
25+
logger = get_logger(__name__)
2226

2327

2428
class AGB:
@@ -58,6 +62,9 @@ def __init__(self, api_key: str = "", cfg: Optional[Config] = None):
5862
self._sessions: Dict[str, Session] = {}
5963
self._lock = Lock()
6064

65+
# Initialize context service
66+
self.context = ContextService(self)
67+
6168
def create(self, params: Optional[CreateSessionParams] = None) -> SessionResult:
6269
"""
6370
Create a new session in the AGB cloud environment.
@@ -78,21 +85,30 @@ def create(self, params: Optional[CreateSessionParams] = None) -> SessionResult:
7885
if params.image_id:
7986
request.image_id = params.image_id
8087

81-
response: CreateSessionResponse = self.client.create_mcp_session(request)
88+
# Flag to indicate if we need to wait for context synchronization
89+
needs_context_sync = False
8290

83-
# Check if response is empty
84-
if response is None:
85-
return SessionResult(
86-
request_id="",
87-
success=False,
88-
error_message="OpenAPI client returned None response",
89-
)
91+
if params.context_syncs:
92+
persistence_data_list = []
93+
for context_sync in params.context_syncs:
94+
if context_sync.policy:
95+
policy_json = json.dumps(context_sync.policy.to_dict(), ensure_ascii=False)
96+
persistence_data_list.append(CreateMcpSessionRequestPersistenceDataList(
97+
context_id=context_sync.context_id,
98+
path=context_sync.path,
99+
policy=policy_json,
100+
))
101+
102+
request.persistence_data_list = persistence_data_list
103+
needs_context_sync = len(persistence_data_list) > 0
104+
105+
response: CreateSessionResponse = self.client.create_mcp_session(request)
90106

91107
try:
92-
print("Response body:")
93-
print(response.to_dict())
108+
logger.debug("Response body:")
109+
logger.debug(response.to_dict())
94110
except Exception:
95-
print(f"Response: {response}")
111+
logger.debug(f"Response: {response}")
96112

97113
# Extract request ID
98114
request_id_attr = getattr(response, "request_id", "")
@@ -120,8 +136,8 @@ def create(self, params: Optional[CreateSessionParams] = None) -> SessionResult:
120136
# ResourceUrl is optional in CreateMcpSession response
121137
resource_url = response.get_resource_url()
122138

123-
print("session_id =", session_id)
124-
print("resource_url =", resource_url)
139+
logger.info(f"session_id = {session_id}")
140+
logger.info(f"resource_url = {resource_url}")
125141

126142
# Create Session object
127143
session = Session(self, session_id)
@@ -134,11 +150,49 @@ def create(self, params: Optional[CreateSessionParams] = None) -> SessionResult:
134150
with self._lock:
135151
self._sessions[session_id] = session
136152

153+
# If we have persistence data, wait for context synchronization
154+
if needs_context_sync:
155+
log_operation_start("Context synchronization", "Waiting for completion")
156+
157+
# Wait for context synchronization to complete
158+
max_retries = 150 # Maximum number of retries
159+
retry_interval = 2 # Seconds to wait between retries
160+
161+
import time
162+
for retry in range(max_retries):
163+
# Get context status data
164+
info_result = session.context.info()
165+
166+
# Check if all context items have status "Success" or "Failed"
167+
all_completed = True
168+
has_failure = False
169+
170+
for item in info_result.context_status_data:
171+
logger.info(f"📁 Context {item.context_id} status: {item.status}, path: {item.path}")
172+
173+
if item.status != "Success" and item.status != "Failed":
174+
all_completed = False
175+
break
176+
177+
if item.status == "Failed":
178+
has_failure = True
179+
logger.error(f"❌ Context synchronization failed for {item.context_id}: {item.error_message}")
180+
181+
if all_completed or not info_result.context_status_data:
182+
if has_failure:
183+
log_warning("Context synchronization completed with failures")
184+
else:
185+
log_operation_success("Context synchronization")
186+
break
187+
188+
logger.info(f"⏳ Waiting for context synchronization, attempt {retry+1}/{max_retries}")
189+
time.sleep(retry_interval)
190+
137191
# Return SessionResult with request ID
138192
return SessionResult(request_id=request_id, success=True, session=session)
139193

140194
except Exception as e:
141-
print("Error calling create_mcp_session:", e)
195+
logger.error(f"Error calling create_mcp_session: {e}")
142196
return SessionResult(
143197
request_id="",
144198
success=False,
@@ -155,66 +209,30 @@ def list(self) -> List[BaseSession]:
155209
with self._lock:
156210
return list(self._sessions.values())
157211

158-
def delete(self, session: Session) -> DeleteResult:
212+
def delete(self, session: BaseSession, sync_context: bool = False) -> DeleteResult:
159213
"""
160214
Delete a session by session object.
161215
162216
Args:
163-
session (Session): The session to delete.
217+
session (BaseSession): The session to delete.
218+
sync_context (bool, optional): Whether to sync context before deletion. Defaults to False.
164219
165220
Returns:
166221
DeleteResult: Result indicating success or failure and request ID.
167222
"""
168223
try:
169-
# Create request to release the session
170-
request = ReleaseSessionRequest(
171-
authorization=f"Bearer {self.api_key}",
172-
session_id=session.session_id,
173-
)
174-
175-
# Make the API call
176-
response = self.client.release_mcp_session(request)
224+
# Delete the session and get the result
225+
delete_result = session.delete(sync_context=sync_context)
177226

178-
# Check if response is empty
179-
if response is None:
180-
return DeleteResult(
181-
request_id="",
182-
success=False,
183-
error_message="OpenAPI client returned None response",
184-
)
227+
with self._lock:
228+
self._sessions.pop(session.session_id, None)
185229

186-
# Check response type, if it's ReleaseSessionResponse, use new parsing method
187-
if hasattr(response, "is_successful"):
188-
# This is a ReleaseSessionResponse object
189-
if response.is_successful():
190-
# Remove from local cache
191-
with self._lock:
192-
self._sessions.pop(session.session_id, None)
193-
194-
request_id_attr = getattr(response, "request_id", "")
195-
return DeleteResult(request_id=request_id_attr or "", success=True)
196-
else:
197-
error_msg = (
198-
response.get_error_message() or "Failed to delete session"
199-
)
200-
request_id_attr = getattr(response, "request_id", "")
201-
return DeleteResult(
202-
request_id=request_id_attr or "",
203-
success=False,
204-
error_message=error_msg,
205-
)
206-
else:
207-
request_id_attr = getattr(response, "request_id", "")
208-
return DeleteResult(
209-
request_id=request_id_attr or "",
210-
success=False,
211-
error_message="Failed to delete session",
212-
)
230+
return delete_result
213231

214232
except Exception as e:
215-
print("Error calling release_mcp_session:", e)
216-
# In case of error, return failure result with error message
233+
logger.error(f"Error calling delete_mcp_session: {e}")
217234
return DeleteResult(
235+
request_id="",
218236
success=False,
219-
error_message=f"Failed to delete session {session.session_id}: {e}",
237+
error_message=f"Failed to delete session: {e}",
220238
)

agb/api/base_service.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from agb.api.models import CallMcpToolRequest
1010
from agb.exceptions import AGBError
1111
from agb.model import OperationResult
12+
from agb.logger import get_logger
13+
14+
logger = get_logger(__name__)
1215

1316

1417
class BaseService:
@@ -85,10 +88,10 @@ def _call_mcp_tool(
8588
if hasattr(response, "is_tool_successful"):
8689
# This is a CallMcpToolResponse object
8790
try:
88-
print("Response body:")
89-
print(json.dumps(response.json_data, ensure_ascii=False, indent=2))
91+
logger.debug("Response body:")
92+
logger.debug(json.dumps(response.json_data, ensure_ascii=False, indent=2))
9093
except Exception:
91-
print(f"Response: {response}")
94+
logger.debug(f"Response: {response}")
9295

9396
if response.is_tool_successful():
9497
# Tool execution successful

0 commit comments

Comments
 (0)