Skip to content

Expose dashboard format controls#118

Closed
zerone0x wants to merge 4 commits into
elara-labs:mainfrom
zerone0x:orbit/d9eb960c-format-controls
Closed

Expose dashboard format controls#118
zerone0x wants to merge 4 commits into
elara-labs:mainfrom
zerone0x:orbit/d9eb960c-format-controls

Conversation

@zerone0x

@zerone0x zerone0x commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add dashboard Input / Output Format controls with compact/balanced/deep/custom input presets
  • persist context_search defaults (top_k/max_tokens) and output level in state.json via /api/format
  • make the MCP server consume updated dashboard defaults without restart while preserving explicit tool arguments

Fixes #100

Tests

  • uv run ruff check src/context_engine/dashboard/server.py src/context_engine/dashboard/_page.py src/context_engine/integration/mcp_server.py tests/dashboard/test_server.py tests/dashboard/test_dashboard_smoke.py
  • uv run pytest -n 1 tests/dashboard/test_server.py tests/dashboard/test_dashboard_smoke.py tests/integration/test_mcp_server.py tests/integration/test_mcp_empty_index.py tests/test_token_efficiency.py

@rajkumarsakthivel rajkumarsakthivel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes — 3 important issues before merge.


Important: _normalize_format_config silently discards user input

When preset is not custom, top_k/max_tokens are overwritten with preset values regardless of what the user typed. A user who picks balanced and types top_k=15 silently gets top_k=10 back — no error, no feedback.

Fix: either reject the request when values diverge from the preset, or auto-switch to custom on the server when they diverge, and return that. Silent overwriting is the worst option.


Important: Two file reads per context_search call

_handle_context_search calls self._load_state() to get the live context_top_k/context_max_tokens, then later _apply_output_compression also calls self._load_state() to pick up the live output_level. That's two synchronous state.json reads on every MCP response.

Consolidate: read state once in _handle_context_search, pass the level down to _apply_output_compression rather than re-reading inside it. Also removes the dead hasattr(self, "_state_path") guard.


Important: list_tools advertises stale defaults

list_tools returns self._default_top_k/self._default_max_tokens (startup values), but _handle_context_search uses live state. An AI assistant that reads the schema to understand defaults sees the wrong values after a dashboard change. Update the schema defaults from live state on each list_tools call, or document the discrepancy explicitly.


Minor issues (can fix in same pass)

  • loadFormatConfig is called inside loadSavings — wrong trigger point. Call it once in the top-level page init instead.
  • saveFormatConfig swallows errors silently: catch(e) { toast('Failed') }. At least log e to the console.
  • _format_config_from_state double-fallback for output_level is confusing. Simplify to one validated fallback.
  • hasattr(req, "model_dump") else req.dict() — Pydantic v2 is already required; dead branch.
  • Missing test: GET /api/format after a POST round-trip to verify persisted values come back correctly.
  • Missing test: POST /api/compression after POST /api/format (and vice versa) to confirm state keys aren't clobbered.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds dashboard controls for configuring context-search input presets (top_k/max_tokens) and output verbosity, persists those defaults to state.json via a new /api/format endpoint, and updates the MCP server to pick up the persisted defaults without requiring a restart.

Changes:

  • Add /api/format GET/POST to read/write persisted format defaults (input preset, top_k/max_tokens, output level).
  • Add “Input / Output Format” controls to the dashboard page and wire them to the new API.
  • Update MCP server to refresh context_search defaults from state.json on each call; add tests covering the new API/UI wiring.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/dashboard/test_server.py Adds API-level tests for /api/format defaults, preset behavior, and clamping/persistence.
tests/dashboard/test_dashboard_smoke.py Extends smoke tests to validate the new format endpoint and presence of UI hooks.
src/context_engine/integration/mcp_server.py Loads persisted defaults and refreshes context_search defaults from state.json at call time; adjusts tool schema defaults.
src/context_engine/dashboard/server.py Implements /api/format and includes format_config in /api/status.
src/context_engine/dashboard/_page.py Adds the dashboard UI panel + JS functions to load/save/reset format settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

lo=500,
hi=50000,
)
top_k = _clamp_top_k(args.get("top_k", default_top_k))
Comment on lines +693 to +697
if hasattr(self, "_state_path"):
state_level = self._load_state().get("output_level")
if state_level in LEVELS:
self._output_level = state_level

Comment on lines +496 to +503
settings = _normalize_format_config(req)
state = _read_state()
state["input_preset"] = settings["input_preset"]
state["context_top_k"] = settings["top_k"]
state["context_max_tokens"] = settings["max_tokens"]
state["output_level"] = settings["output_level"]
(storage_base / "state.json").write_text(json.dumps(state), encoding="utf-8")
return settings
Comment on lines +1557 to +1566
async function loadFormatConfig() {
try {
var r = await fetch(API+'/api/format');
var d = await r.json();
document.getElementById('fmt-input-preset').value = d.input_preset || 'balanced';
document.getElementById('fmt-top-k').value = d.top_k || 10;
document.getElementById('fmt-max-tokens').value = d.max_tokens || 8000;
document.getElementById('fmt-output-level').value = d.output_level || currentLevel || 'standard';
} catch(e) {}
}
Comment on lines +989 to 992
max_tokens = args.get("max_tokens", default_max_tokens)
try:
max_tokens = int(max_tokens)
except (TypeError, ValueError):

@rajkumarsakthivel rajkumarsakthivel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feature, clean design. Two issues preventing merge.

Blocking

1. POST /api/format uses non-atomic write — regression

set_format_config does:

(storage_base / "state.json").write_text(json.dumps(state), encoding="utf-8")

set_compression was just fixed in #123 to use atomic_write_text for exactly this reason (MCP server reads the same file, race causes corruption). The new endpoint needs the same treatment.

2. CI failure — _state_path missing in test mocks

_handle_context_search now calls self._load_state() unconditionally to read context_top_k/context_max_tokens. _load_state accesses self._state_path internally. The test fixtures added in #123 (test_context_search_appends_omitted_note_when_drops_reported, test_context_search_no_note_when_nothing_dropped) construct ContextEngineMCP without going through __init__, so _state_path is absent and the call blows up.

The guard in _apply_output_compression:

if hasattr(self, "_state_path"):

…does not help because _load_state itself accesses the attribute. The same guard (or a try/except) needs to wrap the _load_state() call in _handle_context_search.

Minor

The hasattr(self, "_state_path") guard in _apply_output_compression is a smell — if a real ContextEngineMCP ever lacks _state_path that's a deeper bug. Once the CI fix is in place it can be removed.

loadFormatConfig() is called inside loadSavings(), coupling format config loading to the savings refresh timer. Not wrong, but it means the format panel re-reads from the server on every stats poll. A one-time load on DOMContentLoaded or on panel open would be cleaner.

@rajkumarsakthivel

Copy link
Copy Markdown
Member

Superseded by #133, which includes all the same changes with three fixes: atomic write in /api/format, missing _state_path/_default_top_k in the test helper (caused CI failures), and the unnecessary hasattr guard removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web UI: expose input/output format configuration

3 participants