Calling rietx from a program¶
Two calls carry the whole integration surface. capabilities() says what this
build can do; Refinement.fit does it. There is no third thing to learn, and
no wire format between you and the package: the objects are the API and their
JSON dumps are the wire.
For agents
This chapter is the machine-facing half of the package, and every shape in it
was chosen for a caller that branches on a field rather than one that reads a
sentence. You can read all of it from a REPL, and capabilities() is the
fastest way to answer “did my jax extra take?”, but that is not what it is
for.
The Python API is the surface¶
Up to version 1.2 there was a second one, an agent module holding a single
JSON call that took a request dict and returned an {"ok": …} envelope, plus
its exported JSON Schemas. All of it was retired in 1.3 because it was measured
and not used. Four traced rounds covered 235 instrumented interpreter starts and
5 430 tool calls in one contributor’s bundle, and every agent that had the
choice drove the package directly: read_pattern, Structure,
Refinement.fit, refine_sequential, build_report. It also could not serve
the case it was built for. Its request carried patterns inline, so one lab
pattern is 11 k tokens and a 68-pattern series is about 754 k of them in a
single call.
What replaces it is what those agents already did:
import rietx as rx
data = rx.read_pattern("my_sample.xye")
ref = rx.Refinement(structure, instrument)
result = ref.fit(data, plan="mccusker_default")
print(ref.summary())
Three differences from the envelope are the whole of the upgrade:
A failure raises. Where the envelope answered
{"ok": false, "error": {"code": …}}, the call raises:ValueErrorfor a model or a plan the package refuses,NoPhasesErrorfor a structure with nothing to refine,RuntimeErrorfrom the engine. Catch what you would have branched on.The answer is an object.
RefinementResultis what theresultarm carried,SeriesResultwhatseriescarried,IndexingResultwhatindexingcarried, andSuggestionResultwhatsuggestioncarried. None changed when the envelope went.SuggestionResulthas since gained a requiredCandidateGroup.delta_bic, the one thing in this list a stored 1.2 answer does not validate against.The report is a separate call.
Refinement.reportbuilds theFitReportfor the fit just run, andRefinement.stage_reports_holds the report at every stage boundary whenRefinement.fitwas asked for them withstage_reports=True. A converged report is routinely the least informative one in a run, so ask for the rungs on a run you will actually read.
The answer as JSON¶
Every answer type is a pydantic model, so serialising one is one call and needs nothing from this package on the other side:
from rietx import RefinementResult
"statistics" in RefinementResult.model_fields
model_dump(mode="json") is the form to store and to send: non-finite floats
serialise as the strings "Infinity", "-Infinity" and "NaN", so a parameter
bound of ±inf survives a round-trip (Compatibility).
model_validate reads one back.
For agents
Refinement.summary answers “is this done, and why” in one string, which is the
question a result view is for. Read the diagnostics before the statistics, and
prefer the trajectory over the final report. A plan absorbs an error it cannot
free into whatever it can, converges, and suggests nothing, while its own first
stage named the cause.
Wrapping rietx in a tool call¶
Nothing here is a tool definition, and the package no longer ships one. If you are exposing refinement to a tool-calling model, wrap the Python API yourself and give your tool path arguments rather than inline payloads: a pattern file, a CIF, a project directory. A dedicated tool earns its place when it gates, renders, audits or parallelises something. A refinement driven by an agent that already has a shell needs none of those, and the pattern arrays make the inline form expensive.
For agents
The operating protocol resolves two ways, and both work for someone who only
ran pip install: the hosted copy at DOCS_URL/skill/rietx/SKILL.md, and an
offline copy inside the wheel for a sandbox with no network. Do not construct
either path. capabilities().skill_path and rietx skill --path answer with
whichever this build has, and in a checkout of the repository that is
docs/skill/rietx/ itself.
capabilities()¶
One call answers what this build supports, and every arm is quoted from a live registry rather than typed:
from rietx import capabilities
caps = capabilities()
[plan.name for plan in caps.plans]
[engine.name for engine in caps.indexing_engines]
sorted(caps.features)
Capabilities.backends,Capabilities.solvers,Capabilities.modesandCapabilities.anodesare the dispatch vocabularies. ABackendCapabilitycarriesBackendCapability.name, whether its optional dependency imports here, whether it is experimental, what to install, andBackendCapability.dtype, the precision it computes at. AnAnodeCapabilitycarriesAnodeCapability.name, its ownAnodeCapability.wavelengths,AnodeCapability.kbetafor the contamination check, andAnodeCapability.kalpha1_only, true for theCuKa1-style entries where an incident-side monochromator has left one line rather than two.Capabilities.radiationsis the source kindsInstrument.sourcediscriminates on. Read it beforeCapabilities.anodes, which is a sub-vocabulary of the X-ray entry and says nothing about the others: a program reading the anodes alone would conclude this build does X-rays only. EachRadiationCapabilitycarriesRadiationCapability.kind, the discriminator to write,RadiationCapability.titleandRadiationCapability.scatterer, the one-line statement of what does the scattering and therefore whether the amplitude falls off with Q.RadiationCapability.magnetic_scatteringsays whether a histogram of that radiation carries the magnetic structure factor of a phase declaring a moment (Running a refinement), read off the same table the forward model dispatches on. The other four say how the shape of the source differs, which decides whether a field exists to set at all:RadiationCapability.anomalous_dispersion,RadiationCapability.max_emission_lines(Nonefor unbounded, 1 for a source whose spectrum is one wavelength and can be nothing else),RadiationCapability.polarization_refinable, andRadiationCapability.harmonic_contamination, which is whether that radiation accepts declared λ/n monochromator harmonics (Patterns, structures and instruments). All four are derived from the classes rather than declared, so each flips by itself when its feature lands. The last reads the sameharmonics_supportedattribute the schema’s own refusal reads, so it cannot claim a support the validator denies.Capabilities.plansgives eachPlanCapabilitywithPlanCapability.title,PlanCapability.description,PlanCapability.modesandPlanCapability.when_to_use, so a program can offer the choice in its own UI without hard-coding a list.PLAN_INFOis the same table in the library.Capabilities.reader_formatsis every pattern formatread_patternopens. EachReaderCapabilitycarriesReaderCapability.nameandReaderCapability.titlefor a file dialogue,ReaderCapability.extensions,ReaderCapability.sniff(how the file is recognised),ReaderCapability.sigma(where the uncertainties come from, which differs per vendor),ReaderCapability.options(the keywords that format honours) andReaderCapability.refuses(what it declines, and why). A format with aReaderCapability.refusesstring is one the build recognises in order to decline: “we know what this is and it is the wrong kind of file” is a different answer from “we cannot open this”.Capabilities.reader_optionsis the reader keyword vocabulary itself, build-wide rather than per format, becausescanmeans the same thing in every format that takes it. EachReaderOptionCapabilitygivesReaderOptionCapability.name,ReaderOptionCapability.kind("str"or"int", so a form knows which control to draw) andReaderOptionCapability.help.Capabilities.indexing_enginesandCapabilities.search_presetscarrySearchPresetCapability.typical_secondsandSearchPresetCapability.total_budget_seconds. An indexing search is budgeted, and a caller that has to promise a response time reads it here. Beside them are the search’s own vocabularies:Capabilities.crystal_systemsin the order the scheduler enters them, which is decreasing symmetry and therefore increasing cost;Capabilities.centrings, the Bravais letters each system admits, as a map; andCapabilities.shift_templates, the systematic-shift models the screen can fit.Capabilities.featuresis the feature flags, each derived from the thing it reports (a schema field’s presence, a top-level export’s existence) rather than written as a literaltrue. A flag flips by itself when its feature lands.
Quote this call rather than transcribing its contents. The reader-format list went from five to ten in two days once, and a table in prose would have been wrong by the following week.
Versioned contracts¶
Capabilities reports six version strings. They are separate because they move
independently:
Field |
Versions |
|---|---|
|
the pydantic schemas: the model, the pattern, the result |
|
the |
|
the streaming event ladder |
|
the |
|
the |
|
the indexing gates, caveat and grade vocabularies |
Capabilities.package_version is the seventh string, and the only one that
moves on every release. Store the contract versions alongside any result you store: a
threshold compared across two report_thresholds_version values compares two
different questions.
Capabilities.skill_path is not a version. It is the directory holding the
agent skill this build carries, the operating protocol in the open Agent Skills
format, and None where the build carries none. Point a harness at it, or read
it yourself:
import rietx as rx
from pathlib import Path
print(Path(rx.capabilities().skill_path, "SKILL.md").read_text())
Further reading: the agent skill¶
This chapter describes the surface. The skill covers the half an integrator
cannot derive from a schema: the turn-on order, the degeneracies to memorise,
what to check before believing a number, how to read an abstention, and the
measured findings that should change what a calling agent does. It is
the next chapter, and rietx skill --install puts it where the
harnesses working in your repository will find it.