refactor(usage): close out round-5 nits on PR #871
Non-blocking follow-ups from the merge-ready review: - semantic.py: bound the doc_types copied into embeddings_queries metadata to _USAGE_METADATA_MAX_DOC_TYPES (16). doc_types is caller-supplied with no max_length on the tool signature; capping the stored copy keeps one JSONB row from ballooning (not a billing/injection risk — CP ignores metadata, binds are parameterized). - migration: note that `metric` is intentionally unconstrained Text and that adding a third metric requires keeping the CP-side catalog in sync, else the rollup silently ignores the new rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9c2f9fac46
commit
c89f724585
@@ -55,7 +55,11 @@ def upgrade() -> None:
|
|||||||
postgresql.TIMESTAMP(timezone=True) if is_pg else sa.TIMESTAMP(),
|
postgresql.TIMESTAMP(timezone=True) if is_pg else sa.TIMESTAMP(),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
),
|
),
|
||||||
# Catalog metric: 'embeddings_queries' or 'pages_chunks'.
|
# Catalog metric: 'embeddings_queries' or 'pages_chunks'. Deliberately
|
||||||
|
# an unconstrained Text (no CHECK/enum) — the metric catalog lives in
|
||||||
|
# control-plane config, not the app-DB schema. If a third metric is
|
||||||
|
# ever added, the CP-side catalog must learn it too, or its rollup will
|
||||||
|
# silently ignore the new rows; keep the two in sync.
|
||||||
sa.Column("metric", sa.Text(), nullable=False),
|
sa.Column("metric", sa.Text(), nullable=False),
|
||||||
sa.Column("value", sa.BigInteger(), nullable=False),
|
sa.Column("value", sa.BigInteger(), nullable=False),
|
||||||
# Rawest unit per request (provider, model, tokens, doc_type, ...).
|
# Rawest unit per request (provider, model, tokens, doc_type, ...).
|
||||||
|
|||||||
@@ -46,6 +46,15 @@ from nextcloud_mcp_server.vector.qdrant_client import get_qdrant_client
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Cap how many doc_types we copy into a usage-metering metadata row. doc_types
|
||||||
|
# is caller-supplied and (unlike path_prefixes) has no max_length on the tool
|
||||||
|
# signature, so an adversarial caller could pass a huge list. The CP rollup
|
||||||
|
# ignores metadata for billing (GROUP BY day, metric) and the value is bound
|
||||||
|
# parameterized, so this is not a billing/injection risk — the cap just keeps
|
||||||
|
# a single JSONB row from ballooning. 16 is generous headroom over the handful
|
||||||
|
# of real indexed doc types.
|
||||||
|
_USAGE_METADATA_MAX_DOC_TYPES = 16
|
||||||
|
|
||||||
|
|
||||||
def configure_semantic_tools(mcp: FastMCP):
|
def configure_semantic_tools(mcp: FastMCP):
|
||||||
"""Configure semantic search tools for MCP server."""
|
"""Configure semantic search tools for MCP server."""
|
||||||
@@ -538,7 +547,12 @@ def configure_semantic_tools(mcp: FastMCP):
|
|||||||
metadata={
|
metadata={
|
||||||
"user_id": username,
|
"user_id": username,
|
||||||
"fusion": fusion,
|
"fusion": fusion,
|
||||||
"doc_types": doc_types,
|
# Bounded copy — see _USAGE_METADATA_MAX_DOC_TYPES.
|
||||||
|
"doc_types": (
|
||||||
|
doc_types[:_USAGE_METADATA_MAX_DOC_TYPES]
|
||||||
|
if doc_types
|
||||||
|
else doc_types
|
||||||
|
),
|
||||||
},
|
},
|
||||||
# The outer guard already confirmed the flag, so pass
|
# The outer guard already confirmed the flag, so pass
|
||||||
# enabled=True directly — the store then skips a second
|
# enabled=True directly — the store then skips a second
|
||||||
|
|||||||
Reference in New Issue
Block a user