Class: Mistri::Schema

Inherits:
Object
  • Object
show all
Defined in:
lib/mistri/schema.rb

Overview

Builds tool schemas and validates the portable JSON Schema subset Mistri owns.

Constant Summary collapse

DIALECT =
"https://json-schema.org/draft/2020-12/schema"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSchema

Returns a new instance of Schema.



28
29
30
31
# File 'lib/mistri/schema.rb', line 28

def initialize
  @properties = {}
  @required = []
end

Class Method Details

.build(&block) ⇒ Object

instance_exec binds self to the builder without passing an argument, so zero-arity lambdas and procs behave the same way.

Raises:



20
21
22
23
24
25
26
# File 'lib/mistri/schema.rb', line 20

def self.build(&block)
  raise ConfigurationError, "schema needs a block" unless block

  builder = new
  builder.instance_exec(&block)
  builder.to_h
end

.strict(schema, all_required: false) ⇒ Object

Prepares a schema for constrained decoding without turning a freeform object into one that accepts only {}. Definition compilation happens first so recursive transformation never sees a cyclic or hostile graph.



123
124
125
126
# File 'lib/mistri/schema.rb', line 123

def strict(schema, all_required: false)
  source = Compiled.new(schema)
  strict_at(source.schema, all_required:, path: "$")
end

.task_plan(schema, all_required: false) ⇒ Object

One task plan owns the strict prompt and validation schema. Providers may derive a compatible native constraint, but local validation remains the guarantee when their structured-output subset is narrower.



114
115
116
117
118
# File 'lib/mistri/schema.rb', line 114

def task_plan(schema, all_required: false)
  source = compile_task_schema(schema)
  strict = strict_at(source.schema, all_required:, path: "$")
  TaskPlan.new(Compiled.new(strict))
end

.task_violations(value, schema, path = "$") ⇒ Object

Task mode validates only schemas whose complete assertion contract is implemented locally; provider constrained decoding is an optimization.



107
108
109
# File 'lib/mistri/schema.rb', line 107

def task_violations(value, schema, path = "$")
  task_plan(schema).violations(value, path)
end

.tool_validator(schema, complete: false) ⇒ Object

A Tool keeps this plan for its lifetime. complete is explicit authority for a host validator to own schema interactions core cannot represent.



83
84
85
# File 'lib/mistri/schema.rb', line 83

def tool_validator(schema, complete: false)
  Compiled.new(schema, tool: true, complete: complete)
end

.unsupported_assertions(schema) ⇒ Object

Paths to standard assertions that the zero-dependency validator does not implement. An empty list means local validation owns the contract.



100
101
102
103
# File 'lib/mistri/schema.rb', line 100

def unsupported_assertions(schema)
  compiled = Compiled.new(schema)
  AssertionScanner.new.call(compiled.schema).map(&:first).freeze
end

.validate_definition!(schema) ⇒ Object

Definition checks cover the complete JSON document. Unsupported assertion keywords remain provider guidance in this general API.



69
70
71
72
# File 'lib/mistri/schema.rb', line 69

def validate_definition!(schema)
  Compiled.new(schema)
  schema
end

.validate_mcp!(schema, complete: false) ⇒ Object

MCP schemas get the same stance as host-authored tools: directly reachable portable constraints are enforced locally, while unsupported applicator subtrees stay server guidance. Complete validators own the whole local contract; external references are rejected in either mode.



91
92
93
94
95
96
# File 'lib/mistri/schema.rb', line 91

def validate_mcp!(schema, complete: false)
  compiled = Compiled.new(schema, tool: true, complete: complete)
  AssertionContract.new(complete:, context: "MCP input schema",
                        allow_guidance: true).call(compiled.schema)
  compiled.schema
end

.violations(value, schema, path = "$") ⇒ Object

Returns bounded, model-readable failures for the subset Mistri owns: type, enum, required, properties, additionalProperties, items, and prefixItems. The input is first normalized to the JSON value providers see.



77
78
79
# File 'lib/mistri/schema.rb', line 77

def violations(value, schema, path = "$")
  Compiled.new(schema).violations(value, path)
end

Instance Method Details

#array(name, description = nil, items: { type: "string" }, required: false, **extra) ⇒ Object



44
45
46
47
48
49
50
# File 'lib/mistri/schema.rb', line 44

def array(name, description = nil, items: { type: "string" }, required: false, **extra)
  prop = { type: "array", items: items }
  prop[:description] = description if description
  @properties[name.to_s] = prop.merge(extra)
  @required << name.to_s if required
  nil
end

#object(name, description = nil, required: false, &block) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/mistri/schema.rb', line 52

def object(name, description = nil, required: false, &block)
  prop = block ? self.class.build(&block) : self.class.new.to_h
  prop[:description] = description if description
  @properties[name.to_s] = prop
  @required << name.to_s if required
  nil
end

#to_hObject



60
61
62
63
64
# File 'lib/mistri/schema.rb', line 60

def to_h
  schema = { type: "object", properties: @properties }
  schema[:required] = @required unless @required.empty?
  schema
end