|
2 | 2 |
|
3 | 3 | from typing import Any, Generic, Literal, TypeVar |
4 | 4 |
|
5 | | -from pydantic import BaseModel, Field |
| 5 | +from pydantic import BaseModel, Field, model_validator |
6 | 6 |
|
7 | 7 | # Import message types from core types module |
| 8 | +from memos.log import get_logger |
8 | 9 | from memos.mem_scheduler.schemas.general_schemas import SearchMode |
9 | 10 | from memos.types import MessageDict, MessagesType, PermissionDict |
10 | 11 |
|
11 | 12 |
|
| 13 | +logger = get_logger(__name__) |
12 | 14 | T = TypeVar("T") |
13 | 15 |
|
14 | 16 |
|
@@ -215,7 +217,7 @@ class APISearchRequest(BaseRequest): |
215 | 217 | # ==== Basic inputs ==== |
216 | 218 | query: str = Field( |
217 | 219 | ..., |
218 | | - description=("User search query"), |
| 220 | + description="User search query", |
219 | 221 | ) |
220 | 222 | user_id: str = Field(..., description="User ID") |
221 | 223 |
|
@@ -318,6 +320,41 @@ class APISearchRequest(BaseRequest): |
318 | 320 | description="(Internal) Operation definitions for multi-cube read permissions.", |
319 | 321 | ) |
320 | 322 |
|
| 323 | + @model_validator(mode="after") |
| 324 | + def _convert_deprecated_fields(self) -> "APISearchRequest": |
| 325 | + """ |
| 326 | + Convert deprecated fields to new fields for backward compatibility. |
| 327 | + Ensures full backward compatibility: |
| 328 | + - mem_cube_id → readable_cube_ids |
| 329 | + - moscube is ignored with warning |
| 330 | + - operation ignored |
| 331 | + """ |
| 332 | + # Convert mem_cube_id to readable_cube_ids (new field takes priority) |
| 333 | + if self.mem_cube_id is not None: |
| 334 | + if not self.readable_cube_ids: |
| 335 | + self.readable_cube_ids = [self.mem_cube_id] |
| 336 | + logger.warning( |
| 337 | + "Deprecated field `mem_cube_id` is used in APISearchRequest. " |
| 338 | + "It will be removed in a future version. " |
| 339 | + "Please migrate to `readable_cube_ids`." |
| 340 | + ) |
| 341 | + |
| 342 | + # Reject moscube if set to True (no longer supported) |
| 343 | + if self.moscube: |
| 344 | + logger.warning( |
| 345 | + "Deprecated field `moscube` is used in APISearchRequest. " |
| 346 | + "Legacy MemOSCube pipeline will be removed soon." |
| 347 | + ) |
| 348 | + |
| 349 | + # Warn about operation (internal) |
| 350 | + if self.operation: |
| 351 | + logger.warning( |
| 352 | + "Internal field `operation` is provided in APISearchRequest. " |
| 353 | + "This field is deprecated and ignored." |
| 354 | + ) |
| 355 | + |
| 356 | + return self |
| 357 | + |
321 | 358 |
|
322 | 359 | class APIADDRequest(BaseRequest): |
323 | 360 | """Request model for creating memories.""" |
@@ -426,6 +463,78 @@ class APIADDRequest(BaseRequest): |
426 | 463 | description="(Internal) Operation definitions for multi-cube write permissions.", |
427 | 464 | ) |
428 | 465 |
|
| 466 | + @model_validator(mode="after") |
| 467 | + def _convert_deprecated_fields(self) -> "APIADDRequest": |
| 468 | + """ |
| 469 | + Convert deprecated fields to new fields for backward compatibility. |
| 470 | + This keeps the API fully backward-compatible while allowing |
| 471 | + internal logic to use only the new fields. |
| 472 | +
|
| 473 | + Rules: |
| 474 | + - mem_cube_id → writable_cube_ids |
| 475 | + - memory_content → messages |
| 476 | + - doc_path → messages (input_file) |
| 477 | + - source → info["source"] |
| 478 | + - operation → merged into writable_cube_ids (ignored otherwise) |
| 479 | + """ |
| 480 | + # Convert mem_cube_id to writable_cube_ids (new field takes priority) |
| 481 | + if self.mem_cube_id: |
| 482 | + logger.warning( |
| 483 | + "APIADDRequest.mem_cube_id is deprecated and will be removed in a future version. " |
| 484 | + "Please use `writable_cube_ids` instead." |
| 485 | + ) |
| 486 | + if not self.writable_cube_ids: |
| 487 | + self.writable_cube_ids = [self.mem_cube_id] |
| 488 | + |
| 489 | + # Handle deprecated operation field |
| 490 | + if self.operation: |
| 491 | + logger.warning( |
| 492 | + "APIADDRequest.operation is deprecated and will be removed. " |
| 493 | + "Use `writable_cube_ids` for multi-cube writes." |
| 494 | + ) |
| 495 | + |
| 496 | + # Convert memory_content to messages (new field takes priority) |
| 497 | + if self.memory_content: |
| 498 | + logger.warning( |
| 499 | + "APIADDRequest.memory_content is deprecated. " |
| 500 | + "Use `messages` with a structured message instead." |
| 501 | + ) |
| 502 | + if self.messages is None: |
| 503 | + self.messages = [] |
| 504 | + self.messages.append( |
| 505 | + { |
| 506 | + "type": "text", |
| 507 | + "text": self.memory_content, |
| 508 | + } |
| 509 | + ) |
| 510 | + |
| 511 | + # Handle deprecated doc_path |
| 512 | + if self.doc_path: |
| 513 | + logger.warning( |
| 514 | + "APIADDRequest.doc_path is deprecated. " |
| 515 | + "Use `messages` with an input_file item instead." |
| 516 | + ) |
| 517 | + if self.messages is None: |
| 518 | + self.messages = [] |
| 519 | + self.messages.append( |
| 520 | + { |
| 521 | + "type": "file", |
| 522 | + "file": {"path": self.doc_path}, |
| 523 | + } |
| 524 | + ) |
| 525 | + |
| 526 | + # Convert source to info.source_type (new field takes priority) |
| 527 | + if self.source: |
| 528 | + logger.warning( |
| 529 | + "APIADDRequest.source is deprecated. " |
| 530 | + "Use `info['source_type']` / `info['source_url']` instead." |
| 531 | + ) |
| 532 | + if self.info is None: |
| 533 | + self.info = {} |
| 534 | + self.info.setdefault("source", self.source) |
| 535 | + |
| 536 | + return self |
| 537 | + |
429 | 538 |
|
430 | 539 | class APIChatCompleteRequest(BaseRequest): |
431 | 540 | """Request model for chat operations.""" |
|
0 commit comments