Hardcode Python default empty response
This commit is contained in:
13
README.md
13
README.md
@@ -146,15 +146,14 @@ All routes are local-only and intended for `localhost` usage.
|
|||||||
**Purpose**
|
**Purpose**
|
||||||
|
|
||||||
- Return the next pending instruction, if one exists.
|
- Return the next pending instruction, if one exists.
|
||||||
- If none exists, wait for a configurable duration, then return either an empty response or a configured default response.
|
- If none exists, wait for a configurable duration, then return the server-controlled default response.
|
||||||
- Record agent activity so the UI can infer whether an agent is currently connected/recently active.
|
- Record agent activity so the UI can infer whether an agent is currently connected/recently active.
|
||||||
|
|
||||||
**Suggested input schema**
|
**Suggested input schema**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"agent_id": "optional-string",
|
"agent_id": "optional-string"
|
||||||
"default_response_override": "optional-string"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -194,7 +193,7 @@ All routes are local-only and intended for `localhost` usage.
|
|||||||
"status": "ok",
|
"status": "ok",
|
||||||
"result_type": "default_response",
|
"result_type": "default_response",
|
||||||
"instruction": null,
|
"instruction": null,
|
||||||
"response": "No new instructions available.",
|
"response": "call this tool `get_user_request` again to fetch latest user input...",
|
||||||
"remaining_pending": 0,
|
"remaining_pending": 0,
|
||||||
"waited_seconds": 10
|
"waited_seconds": 10
|
||||||
}
|
}
|
||||||
@@ -250,7 +249,7 @@ Returns current server and agent summary.
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"default_wait_seconds": 10,
|
"default_wait_seconds": 10,
|
||||||
"default_empty_response": "No new instructions available.",
|
"default_empty_response": "call this tool `get_user_request` again to fetch latest user input...",
|
||||||
"agent_stale_after_seconds": 30
|
"agent_stale_after_seconds": 30
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +346,7 @@ Returns editable runtime settings.
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"default_wait_seconds": 10,
|
"default_wait_seconds": 10,
|
||||||
"default_empty_response": "No new instructions available.",
|
"default_empty_response": "call this tool `get_user_request` again to fetch latest user input...",
|
||||||
"agent_stale_after_seconds": 30
|
"agent_stale_after_seconds": 30
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -562,7 +561,7 @@ The server starts on `http://localhost:8000` by default.
|
|||||||
| `DB_PATH` | `data/local_mcp.sqlite3` | SQLite database path |
|
| `DB_PATH` | `data/local_mcp.sqlite3` | SQLite database path |
|
||||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||||
| `DEFAULT_WAIT_SECONDS` | `10` | Default tool wait timeout |
|
| `DEFAULT_WAIT_SECONDS` | `10` | Default tool wait timeout |
|
||||||
| `DEFAULT_EMPTY_RESPONSE` | _(empty)_ | Default response when queue is empty |
|
| `DEFAULT_EMPTY_RESPONSE` | `call this tool \`get_user_request\` again to fetch latest user input...` | Default response when queue is empty |
|
||||||
| `AGENT_STALE_AFTER_SECONDS` | `30` | Seconds of inactivity before agent shown as idle |
|
| `AGENT_STALE_AFTER_SECONDS` | `30` | Seconds of inactivity before agent shown as idle |
|
||||||
| `MCP_STATELESS` | `true` | `true` for stateless sessions (survives restarts, recommended); `false` for stateful |
|
| `MCP_STATELESS` | `true` | `true` for stateless sessions (survives restarts, recommended); `false` for stateful |
|
||||||
| `API_TOKEN` | _(empty)_ | When set, all `/api/*` and `/mcp` requests require `Authorization: Bearer <token>`; web UI prompts for the token on first load |
|
| `API_TOKEN` | _(empty)_ | When set, all `/api/*` and `/mcp` requests require `Authorization: Bearer <token>`; web UI prompts for the token on first load |
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import os
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_EMPTY_RESPONSE = "call this tool `get_user_request` again to fetch latest user input..."
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Settings:
|
class Settings:
|
||||||
# Server
|
# Server
|
||||||
@@ -23,7 +26,7 @@ class Settings:
|
|||||||
|
|
||||||
# MCP / queue behaviour (runtime-editable values are stored in DB; these are defaults for first run)
|
# MCP / queue behaviour (runtime-editable values are stored in DB; these are defaults for first run)
|
||||||
default_wait_seconds: int = 10
|
default_wait_seconds: int = 10
|
||||||
default_empty_response: str = ""
|
default_empty_response: str = DEFAULT_EMPTY_RESPONSE
|
||||||
agent_stale_after_seconds: int = 30
|
agent_stale_after_seconds: int = 30
|
||||||
|
|
||||||
# MCP server name
|
# MCP server name
|
||||||
@@ -53,7 +56,7 @@ def load_settings() -> Settings:
|
|||||||
db_path=os.getenv("DB_PATH", "data/local_mcp.sqlite3"),
|
db_path=os.getenv("DB_PATH", "data/local_mcp.sqlite3"),
|
||||||
log_level=os.getenv("LOG_LEVEL", "INFO"),
|
log_level=os.getenv("LOG_LEVEL", "INFO"),
|
||||||
default_wait_seconds=int(os.getenv("DEFAULT_WAIT_SECONDS", "10")),
|
default_wait_seconds=int(os.getenv("DEFAULT_WAIT_SECONDS", "10")),
|
||||||
default_empty_response=os.getenv("DEFAULT_EMPTY_RESPONSE", ""),
|
default_empty_response=os.getenv("DEFAULT_EMPTY_RESPONSE", DEFAULT_EMPTY_RESPONSE),
|
||||||
agent_stale_after_seconds=int(os.getenv("AGENT_STALE_AFTER_SECONDS", "30")),
|
agent_stale_after_seconds=int(os.getenv("AGENT_STALE_AFTER_SECONDS", "30")),
|
||||||
mcp_server_name=os.getenv("MCP_SERVER_NAME", "local-mcp"),
|
mcp_server_name=os.getenv("MCP_SERVER_NAME", "local-mcp"),
|
||||||
mcp_stateless=_parse_bool(os.getenv("MCP_STATELESS", "true"), default=True),
|
mcp_stateless=_parse_bool(os.getenv("MCP_STATELESS", "true"), default=True),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import threading
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
|
from app.config import DEFAULT_EMPTY_RESPONSE
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Module-level lock for write serialisation (consume atomicity, settings writes, etc.)
|
# Module-level lock for write serialisation (consume atomicity, settings writes, etc.)
|
||||||
@@ -102,7 +104,7 @@ CREATE TABLE IF NOT EXISTS agent_activity (
|
|||||||
|
|
||||||
_DEFAULT_SETTINGS = {
|
_DEFAULT_SETTINGS = {
|
||||||
"default_wait_seconds": "10",
|
"default_wait_seconds": "10",
|
||||||
"default_empty_response": "",
|
"default_empty_response": DEFAULT_EMPTY_RESPONSE,
|
||||||
"agent_stale_after_seconds": "30",
|
"agent_stale_after_seconds": "30",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +121,10 @@ def _seed_defaults(conn: sqlite3.Connection) -> None:
|
|||||||
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
||||||
(key, value),
|
(key, value),
|
||||||
)
|
)
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE settings SET value = ? WHERE key = 'default_empty_response' AND value = ''",
|
||||||
|
(_DEFAULT_SETTINGS["default_empty_response"],),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
logger.debug("Default settings seeded")
|
logger.debug("Default settings seeded")
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from mcp.server.fastmcp import FastMCP, Context
|
from mcp.server.fastmcp import FastMCP, Context
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import DEFAULT_EMPTY_RESPONSE, settings
|
||||||
from app.services import config_service, event_service, instruction_service, status_service
|
from app.services import config_service, event_service, instruction_service, status_service
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -38,9 +38,6 @@ _MAX_WAIT_SECONDS = 86400 # 24 hours
|
|||||||
# multiple keepalive progress updates.
|
# multiple keepalive progress updates.
|
||||||
DEFAULT_WAIT_SECONDS = 50
|
DEFAULT_WAIT_SECONDS = 50
|
||||||
|
|
||||||
# Default response when queue is empty after waiting
|
|
||||||
DEFAULT_EMPTY_RESPONSE = "call this tool `get_user_request` again to fetch latest user input..."
|
|
||||||
|
|
||||||
# Per-agent generation counter — incremented on every new call.
|
# Per-agent generation counter — incremented on every new call.
|
||||||
# The wait loop only consumes an instruction when it holds the latest generation,
|
# The wait loop only consumes an instruction when it holds the latest generation,
|
||||||
# preventing abandoned (timed-out) coroutines from silently consuming queue items.
|
# preventing abandoned (timed-out) coroutines from silently consuming queue items.
|
||||||
@@ -65,7 +62,6 @@ KEEPALIVE_INTERVAL_SECONDS: float = 5.0
|
|||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
async def get_user_request(
|
async def get_user_request(
|
||||||
agent_id: str = "unknown",
|
agent_id: str = "unknown",
|
||||||
default_response_override: Optional[str] = None,
|
|
||||||
ctx: Optional[Context] = None,
|
ctx: Optional[Context] = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -76,8 +72,6 @@ async def get_user_request(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
agent_id: An identifier for this agent instance (used to track connectivity).
|
agent_id: An identifier for this agent instance (used to track connectivity).
|
||||||
default_response_override: Override the server-default empty response text
|
|
||||||
for this single call.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A dict with keys: status, result_type, instruction, response,
|
A dict with keys: status, result_type, instruction, response,
|
||||||
@@ -222,11 +216,7 @@ async def get_user_request(
|
|||||||
status_service.record_agent_activity(agent_id, "empty")
|
status_service.record_agent_activity(agent_id, "empty")
|
||||||
event_service.broadcast("status.changed", {})
|
event_service.broadcast("status.changed", {})
|
||||||
|
|
||||||
empty_response = (
|
empty_response = DEFAULT_EMPTY_RESPONSE
|
||||||
default_response_override
|
|
||||||
if default_response_override is not None
|
|
||||||
else DEFAULT_EMPTY_RESPONSE
|
|
||||||
)
|
|
||||||
result_type = "default_response" if empty_response else "empty"
|
result_type = "default_response" if empty_response else "empty"
|
||||||
|
|
||||||
if _i_am_active():
|
if _i_am_active():
|
||||||
|
|||||||
@@ -7,21 +7,20 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from app.config import DEFAULT_EMPTY_RESPONSE
|
||||||
from app.database import get_conn, get_write_conn
|
from app.database import get_conn, get_write_conn
|
||||||
from app.models import ConfigResponse
|
from app.models import ConfigResponse
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_SETTING_KEYS = {"default_wait_seconds", "default_empty_response", "agent_stale_after_seconds"}
|
_SETTING_KEYS = {"default_wait_seconds", "default_empty_response", "agent_stale_after_seconds"}
|
||||||
|
|
||||||
|
|
||||||
def get_config() -> ConfigResponse:
|
def get_config() -> ConfigResponse:
|
||||||
with get_conn() as conn:
|
with get_conn() as conn:
|
||||||
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
||||||
data = {r["key"]: r["value"] for r in rows}
|
data = {r["key"]: r["value"] for r in rows}
|
||||||
return ConfigResponse(
|
return ConfigResponse(
|
||||||
default_wait_seconds=int(data.get("default_wait_seconds", 10)),
|
default_wait_seconds=int(data.get("default_wait_seconds", 10)),
|
||||||
default_empty_response=data.get("default_empty_response", ""),
|
default_empty_response=data.get("default_empty_response") or DEFAULT_EMPTY_RESPONSE,
|
||||||
agent_stale_after_seconds=int(data.get("agent_stale_after_seconds", 30)),
|
agent_stale_after_seconds=int(data.get("agent_stale_after_seconds", 30)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user