Core spec: allow ai_context on the document root - #323
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for document-scoped guidance and vendor metadata by allowing ai_context and custom_extensions on the core spec document root, aligning the core document root with patterns already used elsewhere (and with the ontology root).
Changes:
- Extend
core-spec/osi-schema.jsondocument root with optionalai_contextandcustom_extensions(root remains closed). - Update the Python reference model and tests to parse/serialize
ai_context/custom_extensionsat the document level. - Update spec/docs to document the new document-level fields and their precedence semantics.
Show a summary per file
| File | Description |
|---|---|
| python/tests/test_models.py | Adds regression tests asserting schema/root parity and round-trip serialization for document-level ai_context and custom_extensions. |
| python/src/ossie/models.py | Adds ai_context and custom_extensions to the OSIDocument Pydantic model. |
| docs/index.md | Updates docs text to include document-level AI context. |
| core-spec/spec.yaml | Documents root-level ai_context / custom_extensions in the YAML spec summary. |
| core-spec/spec.md | Adds a new “Document” section with a worked example and precedence semantics. |
| core-spec/osi-schema.json | Adds the new optional root properties with descriptions; keeps additionalProperties: false. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 6/6 changed files
- Comments generated: 2
- Review effort level: Lite
| version: str = "0.2.0.dev0" | ||
| dialects: Optional[list[OSIDialect]] = None | ||
| vendors: Optional[list[OSIVendor]] = None | ||
| ai_context: Optional[OSIAIContext] = None | ||
| semantic_model: list[OSISemanticModel] | ||
| custom_extensions: Optional[list[OSICustomExtension]] = None |
There was a problem hiding this comment.
Confirmed — the diagnosis is right. Reproduced against this branch:
emitted root keys: ['version', 'dialects', 'vendors', 'semantic_model']
schema errors: ["Additional properties are not allowed ('dialects', 'vendors' were unexpected)"]
Two reasons I'm not making that change here, though:
-
It's pre-existing, not introduced by this PR.
dialects/vendorsare already onOSIDocumentinmain; this PR only addsai_contextandcustom_extensionsalongside them. -
A removal limited to
models.pywould break converters. Rootvendorsis still emitted by the Honeydew converter (converters/honeydew/src/honeydew_osi/converter.py:605-611) and asserted by its tests (test_vendors_roundtrip). Dropping the model fields without the converter side would just relocate the failure.
That work is already in flight in two open PRs — #148 (dbt side + OSIDocument) and #306 (dbt + Honeydew + rejecting root extras). #306 in particular covers the converter half. Issue #322 calls this out explicitly: this PR "goes the other way round — it adds to the schema first and keeps the root closed", deliberately compatible with #148/#306.
So I'd rather let those land the removal than half-do it here and conflict with both. Nothing in this PR makes the situation worse: the root stays additionalProperties: false, and the two optional keys it adds are the only additions.
Side note for whoever picks up #148/#306: OSIVendor has no HONEYDEW member, so OSIDocument.model_validate() currently rejects the Honeydew converter's own output. Schema-side $defs/Vendor is free-form, so custom_extensions is unaffected — it's purely the root vendors field, which those PRs remove anyway.
| # Optional: Document-wide context for AI tools. | ||
| # Applies to every semantic model in this document. Model-level ai_context adds to | ||
| # it and takes precedence where the two conflict. | ||
| # Use this for guidance that governs the document as a whole — a model read in | ||
| # isolation would otherwise have to repeat it. | ||
| # Can be a simple string or a structured object with instructions, | ||
| # synonyms, examples, or vendor-specific keys. | ||
| ai_context: | ||
| instructions: string | ||
| synonyms: | ||
| - string | ||
| examples: | ||
| - string |
There was a problem hiding this comment.
Good catch on the inconsistency — but I've fixed it in the opposite direction, because the string form is the side that's already been agreed to be wrong.
That contradiction is tracked in #141: spec.yaml says ai_context: string at every level, while osi-schema.json ($defs/AIContext) and the canonical examples/tpcds_semantic_model.yaml both use the structured object. The resolution direction there is to tighten spec.yaml up to the schema, not to push the schema's structured form down to string. Declaring the root as string here would move away from that and remove the only structured example in the file.
While checking this I did find a real problem with the block — one that's only visible with more of the file in view. The new keys had landed inside the # Enumerations section (after the --- on line 27, among dialects/datatypes/vendor_name, which are enum definitions, not root keys). Parsed, that section read:
['dialects', 'datatypes', 'vendor_name', 'ai_context', 'custom_extensions', 'semantic_model']
Fixed by giving the root keys their own --- section with a # Document root header, matching how every other section in this file is delimited. It now parses as:
0 -> ['version']
1 -> ['dialects', 'datatypes', 'vendor_name']
2 -> ['ai_context', 'custom_extensions', 'semantic_model']
3 -> ['datasets'] ...
which also pulls semantic_model out of the enum section, where it had been sitting on main.
I did take your "point readers to the authoritative definition" suggestion: the comment now states that the structured form is the one shown and refers to AIContext in osi-schema.json as authoritative for both forms. Aligning the four remaining model-level ai_context: string sites is #141's scope, so I've left those untouched here.
khush-bhatia
left a comment
There was a problem hiding this comment.
Not sure why we would need this ? Also dialects and vendors were removed as top level fields in the document.
…c.yaml
spec.yaml is a multi-document YAML file whose --- separators group the
specification into sections. semantic_model is a document-root key, but it
sits after the # Enumerations separator, so it parses as a member of that
section alongside the enum definitions:
0 -> ['version']
1 -> ['dialects', 'datatypes', 'vendor_name', 'semantic_model']
2 -> ['datasets']
Give the root keys their own section, matching how every other section in the
file is delimited:
0 -> ['version']
1 -> ['dialects', 'datatypes', 'vendor_name']
2 -> ['semantic_model']
3 -> ['datasets']
Comment-only restructuring: no key, value or enum member is added, removed or
changed, and nothing in the repository parses this file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The document root is the only node in the core spec with no ai_context. The key appears on SemanticModel, Dataset, Field, Metric and Relationship; the root is closed with only version and semantic_model. Since semantic_model is a list, guidance that governs every model in a document has nowhere to live. A producer emitting one model per data source must copy shared instructions into each model, where they drift, and a consumer cannot tell those copies apart from genuinely model-specific instruction. Add one optional key to the document root, reusing the existing AIContext definition by reference rather than introducing a new one. Scope only: document-level context applies to every semantic model in the document. How it composes with model-level ai_context is left undefined by this version, because "model-level takes precedence on conflict" has no meaning for the string form of AIContext. An ontology document already carries ai_context on its root and refers to this specification's AIContext definition, so document-wide context is an established shape in Ossie rather than a new one. A test pins both roots to the same definition so they cannot drift. Not a breaking change: no new definitions, required is unchanged, the key is optional, and additionalProperties stays false -- unknown root keys and a wrong-typed ai_context are still rejected. examples/tpcds_semantic_model.yaml validates unchanged. examples/multi_model_ai_context.yaml shows the motivating case: two models, one per data source, each queried in its own dialect, with the publisher's conventions stated once at the root. It is wired into validation CI so it cannot rot. Refs apache#322 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c39d55b to
62e5ee5
Compare
Summary
The document root is the only node in the core spec with no
ai_context. The key appears onSemanticModel,Dataset,Field,MetricandRelationship; the root is closed(
additionalProperties: false) with onlyversionandsemantic_model.Since
semantic_modelis a list, guidance that governs every model in a document hasnowhere to live. This PR adds one optional key to the document root, reusing the existing
definition:
ai_context$ref: #/$defs/AIContextSemantics — scope only. Document-level
ai_contextapplies to every semantic model inthe document. How it composes with model-level
ai_contextis deliberately left undefinedby this version; see "Changes since the first review" below.
The case
examples/multi_model_ai_context.yaml(new) is the motivating document: two models, one perfederated data source, each queried in its own dialect, sharing no keys.
The fiscal-year convention and the "never join across these" rule are properties of the
document. Today the only way to express them is to copy them into every model, which means:
this model" — note
finance's USD rule above, which really is model-specific.The example is wired into
validation-ci.ymlso it cannot rot. That workflow previouslyvalidated only
examples/tpcds_semantic_model.yaml, by hardcoded path, so a new examplewould not otherwise have been checked by anything.
Changes since the first review
Thanks to @khush-bhatia for the review — both points led to changes:
custom_extensions. It resembled re-adding rootvendors, and unlikeai_contextit had no motivating document. The PR is one key now.ai_context"adds to" document-level context and "takes precedence where the two conflict".
AIContextis
oneOf: [string, object], and neither "adds" nor "conflict" is well-defined for thestring form. Scope alone delivers the point of Core spec: document root cannot carry ai_context or custom_extensions #322 — say it once instead of N times — so
composition is left to a later change with its own discussion.
spec.yamlsectioning fix out into core-spec: move semantic_model out of the Enumerations section in spec.yaml #367, since it was noise here.On
dialects/vendorsThese look like the opposite case rather than a precedent against this change.
They were never in the schema root.
mainis{version, semantic_model}withadditionalProperties: false, so a document carrying rootdialectsis rejected byvalidate.pytoday. They are fields on the PythonOssieDocumentmodel only, and #148/#306remove them because the same fact is already carried at the leaf — dialect per expression,
vendor per
custom_extension.Document-wide
ai_contexthas no leaf that already carries it. This PR also keeps the rootclosed and adds nothing to
required.Precedent
An ontology document already carries
ai_contexton its root, and$refs this spec'sdefinition:
The core spec already defines document-wide AI context — the core document root was simply
the only root that never consumed it. A test pins both roots to the same
AIContextdefinition so they cannot drift.
Not a breaking change
$defs— the key reuses the existing definition.requiredis unchanged; the key is optional.additionalProperties: falseis unchanged, so the root stays closed.examples/tpcds_semantic_model.yamlvalidates unchanged, and an absent key does notserialize.
Asserted directly against the edited schema:
ai_contextai_contextas stringai_contextas objectai_context: 42custom_extensionsdialectsStacked on #367
This PR is based on #367 (a 5-line, comment-only sectioning fix in
spec.yaml) and so showstwo commits until that one merges. Only the second commit,
"Core spec: allow ai_context on the document root", belongs to this PR.
Interaction with other open work
dialects,vendors) from the Python model that werenever in the schema. Compatible in substance: this PR goes the other way round — it adds to
the schema first and leaves the root closed. Under Keep converter outputs aligned with root schema #306's
extra="forbid", declared fieldsstill validate.
ai_context: stringbut the JSON schema + canonical example use a structured object #141 tracksspec.yamldeclaringai_context: stringwhile the schema and canonicalexample use the structured object. The root entry added here shows the structured form and
points at
AIContextinossie-schema.jsonas authoritative for both forms; aligning thefour remaining model-level sites is core-spec/spec.yaml declares
ai_context: stringbut the JSON schema + canonical example use a structured object #141's scope and is untouched here.ai_contextshould exist at all. That is a broader question, and afair one — if the direction is to shrink
ai_context, this PR should wait on that outcomerather than iterate. This PR only makes the existing construct available at the one level
where it is missing.
Related Issues
Refs #322
Checklist
Specification
core-spec/and follow the existing structureOntology
ontology/are consistent with spec changes —ontology/is untouched; it already had rootai_contextand this aligns core with itConverters
converters/is updated to reflect spec or ontology changes — not applicable, the key is optional and no converter emits itValidation
validation/are updated if the spec changed —validate.pyis schema-driven and needed no change; verified positive, negative and back-compat cases against itexamples/multi_model_ai_context.yamlis now validated in CIDocumentation
docs/is updated to reflect any user-facing changesspec.md, and a full example underexamples/CONTRIBUTING.mdis updated if the contribution process changed — not applicableExamples
examples/are added or updated for any new spec constructs —multi_model_ai_context.yamladded;tpcds_semantic_model.yamlintentionally left unchanged to demonstrate back-compatTests
pytest/ CI green) —python/tests9 -> 12 passing;validation/test_validate.py34 passing;validation/tests/10 passingAIContextdefinitionCompliance