2026-04-28 09:22:19 -07:00
|
|
|
"""``GET /api/agent/flags``: read-only feature-flag status.
|
|
|
|
|
|
|
|
|
|
Surfaces :class:`AgentFeatureFlags` to the frontend so the UI can:
|
|
|
|
|
|
|
|
|
|
* Render conditional surfaces (e.g. show the action-log button only when
|
|
|
|
|
``enable_action_log`` is on).
|
|
|
|
|
* Display an admin diagnostics card so operators can verify which
|
|
|
|
|
middleware tier is active without shelling into the box.
|
|
|
|
|
|
|
|
|
|
The endpoint is *read-only*. Flipping flags requires an env-var change
|
|
|
|
|
plus a process restart — by design, since the values are baked into the
|
|
|
|
|
agent factory at build time. The route does not require any special
|
|
|
|
|
permission (any authenticated user can see them) since the flag values
|
|
|
|
|
do not leak data, and the UI surfaces are conditionally rendered based
|
|
|
|
|
on them anyway.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from dataclasses import asdict
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
refactor(agents): introduce chat/ category; dissolve top-level agents/shared
Recursive shared-folder rule: a shared/ must be shared by ALL siblings at its
level. The kernel (context, compaction, retry_after, web_search) was shared by
only 2 of the agents -- anonymous_chat + multi_agent_chat -- never by podcaster
or video_presentation. Those 2 are the "chat" category, so their shared code
belongs in that category's shared/, not the top-level one.
app/agents/anonymous_chat/ -> app/agents/chat/anonymous_chat/
app/agents/multi_agent_chat/ -> app/agents/chat/multi_agent_chat/
app/agents/shared/ -> app/agents/chat/shared/ (anon<->mac kernel)
Top-level app/agents/shared/ is gone: nothing was shared across all three
categories (chat / podcaster / video_presentation).
~289 import sites rewritten (app.agents.{anonymous_chat,multi_agent_chat,shared}
-> app.agents.chat.*); all moves are git renames (history preserved).
app/agents/ now: chat/, podcaster/, video_presentation/, runtime/.
2026-06-05 12:54:02 +02:00
|
|
|
from app.agents.chat.multi_agent_chat.shared.feature_flags import (
|
refactor(agents): move mac-only modules out of the cross-agent shared kernel
app/agents/shared/ is a sibling of anonymous_chat/podcaster/multi_agent_chat/
video_presentation, so it should only hold code shared across 2+ of those
agents. In practice podcaster and video_presentation import nothing from it,
and anonymous_chat needs only context + compaction + retry_after + web_search.
Everything else was multi_agent_chat-only (the boundary just passes through).
Move the multi_agent_chat-only cluster into multi_agent_chat/shared/ (files
moved verbatim via git rename; ~116 import sites rewritten):
errors, feature_flags, filesystem_selection, path_resolver, prompt_caching,
sandbox, llm_config, mention_resolver
middleware/busy_mutex, middleware/kb_persistence
busy_mutex/llm_config/mention_resolver are boundary-only but import the moved
modules, so they were folded in to avoid a backwards shared -> multi_agent_chat
dependency. main_agent builders now import the impls directly; the shared
middleware barrel keeps only the genuinely-shared compaction + retry_after.
Also delete the dead leftover shared/plugins and shared/skills dirs (live
copies already live under main_agent/).
Remaining in app/agents/shared/: context, system_prompt(+prompts), checkpointer,
middleware/{compaction,retry_after,dedup_tool_calls}, tools/. checkpointer and
system_prompt are boundary-only infra pending a dedicated home decision.
2026-06-05 12:30:15 +02:00
|
|
|
AgentFeatureFlags,
|
|
|
|
|
get_flags,
|
|
|
|
|
)
|
2026-05-02 23:10:48 -07:00
|
|
|
from app.config import config
|
2026-04-28 09:22:19 -07:00
|
|
|
from app.db import User
|
|
|
|
|
from app.users import current_active_user
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AgentFeatureFlagsRead(BaseModel):
|
|
|
|
|
"""Mirror of :class:`AgentFeatureFlags`. Updated together with it."""
|
|
|
|
|
|
|
|
|
|
disable_new_agent_stack: bool
|
|
|
|
|
|
|
|
|
|
enable_context_editing: bool
|
|
|
|
|
enable_compaction_v2: bool
|
|
|
|
|
enable_retry_after: bool
|
|
|
|
|
enable_model_fallback: bool
|
|
|
|
|
enable_model_call_limit: bool
|
|
|
|
|
enable_tool_call_limit: bool
|
|
|
|
|
enable_tool_call_repair: bool
|
|
|
|
|
enable_doom_loop: bool
|
|
|
|
|
|
|
|
|
|
enable_permission: bool
|
|
|
|
|
enable_busy_mutex: bool
|
|
|
|
|
enable_llm_tool_selector: bool
|
|
|
|
|
|
|
|
|
|
enable_skills: bool
|
|
|
|
|
enable_specialized_subagents: bool
|
|
|
|
|
enable_kb_planner_runnable: bool
|
|
|
|
|
|
|
|
|
|
enable_action_log: bool
|
|
|
|
|
enable_revert_route: bool
|
|
|
|
|
|
|
|
|
|
enable_plugin_loader: bool
|
|
|
|
|
|
|
|
|
|
enable_otel: bool
|
|
|
|
|
|
2026-05-02 23:10:48 -07:00
|
|
|
enable_desktop_local_filesystem: bool
|
|
|
|
|
|
2026-04-28 09:22:19 -07:00
|
|
|
@classmethod
|
2026-04-28 21:37:51 -07:00
|
|
|
def from_flags(cls, flags: AgentFeatureFlags) -> AgentFeatureFlagsRead:
|
2026-04-28 09:22:19 -07:00
|
|
|
# asdict() avoids missing-field bugs when AgentFeatureFlags grows.
|
2026-05-02 23:10:48 -07:00
|
|
|
return cls(
|
|
|
|
|
**asdict(flags),
|
|
|
|
|
enable_desktop_local_filesystem=config.ENABLE_DESKTOP_LOCAL_FILESYSTEM,
|
|
|
|
|
)
|
2026-04-28 09:22:19 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/agent/flags", response_model=AgentFeatureFlagsRead)
|
|
|
|
|
async def get_agent_flags(
|
|
|
|
|
_user: User = Depends(current_active_user),
|
|
|
|
|
) -> AgentFeatureFlagsRead:
|
|
|
|
|
return AgentFeatureFlagsRead.from_flags(get_flags())
|