Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ agent_repository_frontend
.tokensave

.playwright-mcp/
frontend/test-results/
1 change: 1 addition & 0 deletions .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sonar.cpd.exclusions=deploy/sql/init.sql,deploy/sql/migrations/v2.4.0_0723_add_memory_dreaming_audit.sql,deploy/sql/migrations/v2.4.0_0723_add_memory_dreaming_version.sql
27 changes: 27 additions & 0 deletions backend/agents/create_agent_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ def _format_long_term_memory_prompt(search_context: Any, language: str) -> str:
return "\n\n".join(sections)


def _get_active_dreaming_version(
tenant_id: str, user_id: str, agent_id: str
) -> Optional[Dict[str, Any]]:
"""Keep the Dreaming repository optional in isolated SDK-style tests."""
try:
from database.memory_dreaming_db import get_active_version

return get_active_version(tenant_id, user_id, agent_id)
except ModuleNotFoundError:
logger.debug("Dreaming repository is unavailable in this runtime")
return None


# Safe fallback for context-manager token_threshold when no capacity is known.
# Used only when the resolver fails (uncataloged model with no operator-supplied
# hard capacity). Sized to cover the typical 32K-context band shared by the
Expand Down Expand Up @@ -830,6 +843,20 @@ async def create_agent_config(
memory_context = build_memory_context(
user_id, tenant_id, agent_id, skip_query=not allow_memory_search
)
if allow_memory_search and memory_context.user_config.memory_switch:
active_dreaming_version = _get_active_dreaming_version(
str(tenant_id), str(user_id), str(agent_id)
)
if active_dreaming_version:
memory_list.append(
{
"memory": active_dreaming_version["published_content"],
"memory_level": "user",
"memory_type": "long_term",
"score": 1.0,
"dreaming_version_id": active_dreaming_version["version_id"],
}
)

# Append active memory tools if memory is enabled
if memory_context.user_config.memory_switch:
Expand Down
17 changes: 17 additions & 0 deletions backend/apps/config_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
from apps.memory_config_app import router as memory_config_router
from apps.memory_record_app import router as memory_record_router
from apps.quota_app import tenant_quota_router, platform_quota_router
from apps.memory_record_app import router as memory_record_router
from apps.memory_dreaming_app import router as memory_dreaming_router
from consts.const import IS_SPEED_MODE
from services.prompt_template_service import sync_system_default_prompt_template

Expand All @@ -62,6 +64,19 @@ async def sync_default_prompt_template_on_startup():
except Exception as exc:
logger.error(f"Failed to sync system default prompt template: {str(exc)}")


@app.on_event("startup")
async def start_dreaming_scheduler():
from services.memory_dreaming_scheduler import dreaming_scheduler
await dreaming_scheduler.start()


@app.on_event("shutdown")
async def stop_dreaming_scheduler():
from services.memory_dreaming_scheduler import dreaming_scheduler
await dreaming_scheduler.stop()


app.include_router(model_manager_router)
app.include_router(config_sync_router)
app.include_router(agent_router)
Expand Down Expand Up @@ -111,3 +126,5 @@ async def sync_default_prompt_template_on_startup():
app.include_router(memory_record_router)
app.include_router(tenant_quota_router)
app.include_router(platform_quota_router)
app.include_router(memory_record_router)
app.include_router(memory_dreaming_router)
32 changes: 32 additions & 0 deletions backend/apps/memory_config_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- DELETE `/memory/config/disable_useragent/{agent_id}`: Remove a disabled user-agent id.
"""
import logging
from datetime import datetime
from typing import Any, Optional

from http import HTTPStatus
Expand All @@ -26,6 +27,7 @@
from consts.const import (
MEMORY_AGENT_SHARE_KEY,
MEMORY_SWITCH_KEY,
DREAMING_SWITCH_KEY,
BOOLEAN_TRUE_VALUES,
)
from consts.model import MemoryAgentShareMode
Expand All @@ -38,7 +40,9 @@
remove_disabled_useragent_id,
set_agent_share,
set_memory_switch,
set_dreaming_switch,
)
from database import memory_dreaming_db
from services.memory_record_service import (
get_tenant_memory_index_name,
is_tenant_embedding_configured,
Expand Down Expand Up @@ -112,6 +116,9 @@
raise HTTPException(status_code=HTTPStatus.NOT_ACCEPTABLE,
detail="Invalid value for MEMORY_AGENT_SHARE (expected always/ask/never)")
ok = set_agent_share(user_id, mode)
elif key == DREAMING_SWITCH_KEY:
enabled = bool(value) if isinstance(value, bool) else str(value).lower() in BOOLEAN_TRUE_VALUES
ok = set_dreaming_switch(user_id, enabled)
else:
raise HTTPException(status_code=HTTPStatus.NOT_ACCEPTABLE,
detail="Unsupported configuration key")
Expand All @@ -122,6 +129,31 @@
detail="Failed to update configuration")


@router.post("/config/dreaming")
def set_dreaming_config(
enabled: bool = Body(...),

Check warning on line 134 in backend/apps/memory_config_app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "Annotated" type hints for FastAPI dependency injection

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AZ-joJ43K0YJA_tpE2dU&open=AZ-joJ43K0YJA_tpE2dU&pullRequest=3509
delete_history: bool = Body(False),

Check warning on line 135 in backend/apps/memory_config_app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "Annotated" type hints for FastAPI dependency injection

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AZ-joJ43K0YJA_tpE2dV&open=AZ-joJ43K0YJA_tpE2dV&pullRequest=3509
authorization: Optional[str] = Header(None),

Check warning on line 136 in backend/apps/memory_config_app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "Annotated" type hints for FastAPI dependency injection

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AZ-joJ43K0YJA_tpE2dW&open=AZ-joJ43K0YJA_tpE2dW&pullRequest=3509
):
user_id, tenant_id = get_current_user_id(authorization)
if not set_dreaming_switch(user_id, enabled):
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Failed to update Dreaming")
if not enabled:
schedule = memory_dreaming_db.get_schedule(tenant_id, user_id, "__user__")
if schedule:
memory_dreaming_db.upsert_schedule(
tenant_id, user_id, "__user__", enabled=False,
rule_type=schedule["rule_type"], timezone_name=schedule["timezone"],
start_at=datetime.fromisoformat(schedule["start_at"]),
cron_expr=schedule["cron_expr"],
interval_seconds=schedule["interval_seconds"],
next_fire_at=None, actor_user_id=user_id,
)
if delete_history:
memory_dreaming_db.delete_user_dreaming_history(tenant_id, user_id)
return {"success": True}


@router.post("/config/disable_agent")
def add_disable_agent(
agent_id: str = Body(..., embed=True),
Expand Down
Loading
Loading