Skip to content

DEV-1261: Meta fields#33

Merged
AivanF merged 4 commits intomainfrom
aivan/dev-1261-slayer-metadata-in-models-mes-dim
Apr 17, 2026
Merged

DEV-1261: Meta fields#33
AivanF merged 4 commits intomainfrom
aivan/dev-1261-slayer-metadata-in-models-mes-dim

Conversation

@AivanF
Copy link
Copy Markdown
Collaborator

@AivanF AivanF commented Apr 16, 2026

Summary by CodeRabbit

  • New Features
    • Models, dimensions, and measures can include an optional JSON "meta" field; metadata is persisted and can be created, updated, or cleared via MCP, HTTP API, and CLI.
  • API Changes
    • Query responses now expose field metadata under an "attributes" object split into "dimensions" and "measures" when present.
  • Documentation
    • Docs updated with examples and usage notes for the optional "meta" field.
  • Tests
    • Integration and formatting tests updated to the new attributes structure.

@AivanF AivanF requested a review from ZmeiGorynych April 16, 2026 12:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds optional JSON meta on models, dimensions, and measures; propagates field metadata as attributes split into dimensions and measures across engine, API, and client; extends MCP edit_model to update/clear meta; refactors MCP formatting helpers and updates tests accordingly.

Changes

Cohort / File(s) Summary
Docs
CLAUDE.md, docs/concepts/models.md
Documented optional meta: dict on top-level models, dimensions[], and measures[] with examples and persistence/editing notes.
Core models
slayer/core/models.py
Added meta: Optional[Dict[str, Any]] = None to Dimension, Measure, and SlayerModel; added Dict typing import.
MCP server & formatting
slayer/mcp/server.py
Introduced _UNSET sentinel; edit_model(..., meta: Optional[Dict[str,Any]] = _UNSET) supports unchanged/update/clear semantics; replaced _format_meta with _format_field_meta and _format_attributes.
API response types
slayer/api/server.py
Replaced flat meta in QueryResponse with attributes: AttributesResponse containing dimensions and measures maps; /query now constructs attributes only when non-empty.
Client parsing
slayer/client/slayer_client.py
Parse result["attributes"] into ResponseAttributes (dimensions/measures) using _parse_meta_dict; return SlayerResponse.attributes instead of legacy meta.
Engine metadata propagation
slayer/engine/query_engine.py
Added ResponseAttributes dataclass (dimensions/measures + get()); SlayerResponse now uses attributes; query engine builds separate dim/measure metadata and returns attributes.
Tests
tests/integration/test_integration.py, tests/test_format_propagation.py
Updated tests to read metadata from attributes.dimensions / attributes.measures and to exercise _format_attributes rendering.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant MCP_Server as MCP_Server
  participant ModelObj as ModelObj
  participant Storage as Storage

  Client->>MCP_Server: edit_model(model_id, meta=payload)
  MCP_Server->>ModelObj: load(model_id)
  alt meta is _UNSET
    MCP_Server->>ModelObj: (no change)
  else meta is None
    MCP_Server->>ModelObj: set model.meta = None
    MCP_Server-->>MCP_Server: record "cleared meta"
  else meta is dict
    MCP_Server->>ModelObj: set model.meta = dict
    MCP_Server-->>MCP_Server: record "updated meta"
  end
  MCP_Server->>Storage: persist(ModelObj)
  MCP_Server->>Client: return result
Loading
sequenceDiagram
  participant Client as Client
  participant API as API_Server
  participant Engine as QueryEngine
  participant Storage as Storage

  Client->>API: /query(request)
  API->>Engine: execute(query)
  Engine->>Storage: read schema & metadata
  Engine-->>API: SlayerResponse(attributes: {dimensions, measures}, rows, sql)
  API-->>Client: QueryResponse(attributes=..., rows, sql)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • ZmeiGorynych

Poem

🐇 I munched on JSON by lantern light,

Meta seeds tucked cozy and tight,
None will clear, a map will stay,
Hop, edit, save, then bounce away. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'DEV-1261: Meta fields' is vague and generic, using non-descriptive terms that don't clearly convey the specific changes made in the changeset. Consider a more descriptive title that specifies the change, such as 'Add optional meta field to models, dimensions, and measures' or 'Restructure query response metadata into dimensions and measures attributes'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aivan/dev-1261-slayer-metadata-in-models-mes-dim

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slayer/mcp/server.py`:
- Line 451: The current parameter handling prevents callers from explicitly
clearing metadata because the function signatures use extra: Optional[Dict[str,
Any]] = None and checks like "if extra is not None" only update when non-None;
change to use a unique sentinel (e.g., _UNSET = object()) as the default for the
extra parameter (extra: Optional[Dict[str, Any]] = _UNSET) and replace checks
"if extra is not None" with "if extra is not _UNSET" so callers can pass None to
explicitly clear extra while distinguishing unspecified calls; update all
relevant signatures and conditionals referenced (the places around lines with
extra param and the checks at lines ~464-465 and ~514-516) to use the sentinel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b5c484a4-47dd-471b-bd0e-e775acd07c41

📥 Commits

Reviewing files that changed from the base of the PR and between bdce4d8 and 3bebab6.

📒 Files selected for processing (4)
  • CLAUDE.md
  • docs/concepts/models.md
  • slayer/core/models.py
  • slayer/mcp/server.py

Comment thread slayer/mcp/server.py Outdated
@AivanF AivanF force-pushed the aivan/dev-1261-slayer-metadata-in-models-mes-dim branch from 3bebab6 to f0e9d72 Compare April 16, 2026 12:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slayer/client/slayer_client.py`:
- Around line 68-75: The parser _parse_meta_dict currently constructs
FieldMetadata only with label (loss of API-provided number formatting); update
_parse_meta_dict to also extract and pass format (e.g., format=v.get("format"))
into the FieldMetadata constructor so ResponseAttributes.dimensions and
.measures retain formatting metadata from attrs_raw/result; if FieldMetadata has
no format parameter, add one or set the format attribute on the FieldMetadata
instance before returning.

In `@slayer/mcp/server.py`:
- Around line 515-517: In edit_model, the conditional appending to changes uses
an undefined name extra causing a NameError; change the conditional to reference
meta instead (i.e., append "updated meta" if meta is not None else "cleared
meta") so the block that sets model.meta and appends to changes uses the correct
variable (model.meta, meta, changes).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aad4acc0-74a4-4d43-92d6-081aaeea2e9c

📥 Commits

Reviewing files that changed from the base of the PR and between f0e9d72 and 395a7c0.

📒 Files selected for processing (9)
  • CLAUDE.md
  • docs/concepts/models.md
  • slayer/api/server.py
  • slayer/client/slayer_client.py
  • slayer/core/models.py
  • slayer/engine/query_engine.py
  • slayer/mcp/server.py
  • tests/integration/test_integration.py
  • tests/test_format_propagation.py
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md

Comment thread slayer/client/slayer_client.py
Comment thread slayer/mcp/server.py Outdated
@AivanF AivanF changed the title DEV-1261: Extra fields DEV-1261: Meta fields Apr 16, 2026
@AivanF AivanF force-pushed the aivan/dev-1261-slayer-metadata-in-models-mes-dim branch from 395a7c0 to 59393ef Compare April 16, 2026 14:43
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
slayer/client/slayer_client.py (1)

68-69: ⚠️ Potential issue | 🟠 Major

Preserve format while parsing attribute metadata.

Line 69 still constructs FieldMetadata with only label, so API-provided format metadata is dropped for remote clients.

Suggested fix
+        from slayer.core.format import NumberFormat
+
         def _parse_meta_dict(d: dict) -> Dict[str, FieldMetadata]:
-            return {k: FieldMetadata(label=v.get("label")) for k, v in (d or {}).items()}
+            parsed: Dict[str, FieldMetadata] = {}
+            for k, v in (d or {}).items():
+                fmt = NumberFormat.model_validate(v["format"]) if v.get("format") else None
+                parsed[k] = FieldMetadata(label=v.get("label"), format=fmt)
+            return parsed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slayer/client/slayer_client.py` around lines 68 - 69, The _parse_meta_dict
function currently constructs FieldMetadata using only the label and discards
any API-provided format; update _parse_meta_dict to pass the format (e.g.,
v.get("format")) into the FieldMetadata constructor alongside label so the
FieldMetadata instances preserve the format metadata returned by the API (refer
to _parse_meta_dict and FieldMetadata to locate the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slayer/api/server.py`:
- Around line 79-87: Guard against result.attributes being None before accessing
attrs.dimensions or attrs.measures: check whether result.attributes is truthy
(or explicitly None) after assigning attrs = result.attributes and only call
_convert_meta and construct AttributesResponse when attrs exists; otherwise set
attributes = None. Update the conditional that currently reads "if
attrs.dimensions or attrs.measures" to first verify attrs is not None (or use
safe defaults like empty dicts) so the references to attrs.dimensions and
attrs.measures in the AttributesResponse construction are only executed when
attrs is present. Ensure you modify the code around the symbols attrs,
result.attributes, _convert_meta, and AttributesResponse.

In `@slayer/client/slayer_client.py`:
- Around line 66-67: The local import of ResponseAttributes in slayer_client.py
should be moved to the module/top-level imports: remove the inline "from
slayer.engine.query_engine import ResponseAttributes" and add that import
alongside the file's other top-level imports; if this was done to avoid a
circular import, instead guard it with a typing.TYPE_CHECKING import or import
inside an if TYPE_CHECKING block so the symbol is still available for type
annotations while keeping runtime imports at module scope.

---

Duplicate comments:
In `@slayer/client/slayer_client.py`:
- Around line 68-69: The _parse_meta_dict function currently constructs
FieldMetadata using only the label and discards any API-provided format; update
_parse_meta_dict to pass the format (e.g., v.get("format")) into the
FieldMetadata constructor alongside label so the FieldMetadata instances
preserve the format metadata returned by the API (refer to _parse_meta_dict and
FieldMetadata to locate the change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 475682c9-3272-4b71-924f-874db47c17c4

📥 Commits

Reviewing files that changed from the base of the PR and between 395a7c0 and 59393ef.

📒 Files selected for processing (9)
  • CLAUDE.md
  • docs/concepts/models.md
  • slayer/api/server.py
  • slayer/client/slayer_client.py
  • slayer/core/models.py
  • slayer/engine/query_engine.py
  • slayer/mcp/server.py
  • tests/integration/test_integration.py
  • tests/test_format_propagation.py
✅ Files skipped from review due to trivial changes (1)
  • tests/test_format_propagation.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/concepts/models.md
  • CLAUDE.md
  • slayer/engine/query_engine.py

Comment thread slayer/api/server.py Outdated
Comment thread slayer/client/slayer_client.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
slayer/api/server.py (1)

39-41: Use Field(default_factory=dict) for mutable model defaults.

Lines 40–41 use literal {} defaults. In Pydantic v2, Field(default_factory=dict) is the recommended approach for mutable defaults; it clearly signals intent and aligns with Python standards, avoiding reliance on Pydantic's internal deep-copy mechanism.

♻️ Proposed refactor
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
@@
 class AttributesResponse(BaseModel):
-    dimensions: Dict[str, FieldMetadataResponse] = {}
-    measures: Dict[str, FieldMetadataResponse] = {}
+    dimensions: Dict[str, FieldMetadataResponse] = Field(default_factory=dict)
+    measures: Dict[str, FieldMetadataResponse] = Field(default_factory=dict)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slayer/api/server.py` around lines 39 - 41, The AttributesResponse Pydantic
model currently uses literal {} for the mutable defaults on the dimensions and
measures fields; replace those defaults with Field(default_factory=dict) (import
Field from pydantic) so the model uses proper factory-based defaults in Pydantic
v2. Update the AttributesResponse class to declare dimensions: Dict[str,
FieldMetadataResponse] = Field(default_factory=dict) and measures: Dict[str,
FieldMetadataResponse] = Field(default_factory=dict), leaving types and
FieldMetadataResponse reference unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@slayer/api/server.py`:
- Around line 39-41: The AttributesResponse Pydantic model currently uses
literal {} for the mutable defaults on the dimensions and measures fields;
replace those defaults with Field(default_factory=dict) (import Field from
pydantic) so the model uses proper factory-based defaults in Pydantic v2. Update
the AttributesResponse class to declare dimensions: Dict[str,
FieldMetadataResponse] = Field(default_factory=dict) and measures: Dict[str,
FieldMetadataResponse] = Field(default_factory=dict), leaving types and
FieldMetadataResponse reference unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24475d96-c07d-488d-94ae-22cb1c916eda

📥 Commits

Reviewing files that changed from the base of the PR and between 59393ef and b55e608.

📒 Files selected for processing (2)
  • slayer/api/server.py
  • slayer/client/slayer_client.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • slayer/client/slayer_client.py

@AivanF AivanF merged commit cc1bad2 into main Apr 17, 2026
3 checks passed
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.

1 participant