Configuration API¶
The versioned JSON loader and what it produces: load_config and loads_config parse
and validate a file into an AnyInferConfig, and dump_config and dumps_config write
the same format back. The prose reference for the file format lives in
Shared configuration.
anyinfer.CONFIG_FORMAT_VERSION
module-attribute
¶
CONFIG_FORMAT_VERSION = 1
The configuration format version written and understood by this release.
anyinfer.MAX_CONFIG_BYTES
module-attribute
¶
MAX_CONFIG_BYTES = 1024 * 1024
Maximum accepted configuration size.
anyinfer.AnyInferConfig
dataclass
¶
AnyInferConfig(
providers: tuple[ProviderSettings, ...] = (),
route: Route | None = None,
format_version: int = CONFIG_FORMAT_VERSION,
context: ContextTuning = DEFAULT_TUNING,
history: HistoryPolicy | None = None,
cache: CachePolicy | None = None,
repair: Repair | None = None,
observers: tuple[ObserverSpec, ...] = (),
arena: ArenaPolicy | None = None,
arenas: Mapping[str, ArenaPolicy] = dict(),
mcp: tuple[MCPServer, ...] = (),
operation_routes: Mapping[str, Route] = dict(),
)
Validated configuration shared by every AnyInfer integration surface.
Pass providers and route directly to Client or AsyncClient. The same object is
used by the command-line runner and OpenAI-compatible sidecar.
Attributes:
| Name | Type | Description |
|---|---|---|
providers |
tuple[ProviderSettings, ...]
|
Configured provider instances, in declaration order. |
route |
Route | None
|
Default fallback route, when one was configured. |
format_version |
int
|
Parsed file-format version. |
context |
ContextTuning
|
Advanced context-reduction settings from the optional |
history |
HistoryPolicy | None
|
Conversation-compaction policy from the optional |
cache |
CachePolicy | None
|
Prompt-cache placement from the optional |
mcp |
tuple[MCPServer, ...]
|
Model Context Protocol servers described by the optional |
observers |
tuple[ObserverSpec, ...]
|
Telemetry sinks described by the optional |
repair |
Repair | None
|
Bounded schema-repair budget from the optional |
operation_routes |
Mapping[str, Route]
|
Per-operation default routes from the optional
|
anyinfer.load_config ¶
load_config(
path: str | Path,
*,
registry: ProviderRegistry | None = None,
) -> AnyInferConfig
Read and validate an AnyInfer JSON configuration file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
File to read. |
required |
registry
|
ProviderRegistry | None
|
Provider registry used to validate setup-field names. Defaults to the process-wide registry, including installed third-party providers. |
None
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If the file cannot be read or does not match the shared format. |
anyinfer.loads_config ¶
loads_config(
text: str,
*,
source: str = "<string>",
registry: ProviderRegistry | None = None,
) -> AnyInferConfig
Parse and validate AnyInfer configuration from a JSON string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
UTF-8 JSON text. |
required |
source
|
str
|
Human-readable source name included in validation errors. |
'<string>'
|
registry
|
ProviderRegistry | None
|
Provider registry used to validate setup-field names. |
None
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If the text is too large, invalid JSON, or has invalid fields. |
anyinfer.dumps_config ¶
dumps_config(
config: AnyInferConfig, *, comments: bool = False
) -> str
Render a configuration as the JSON text loads_config accepts.
The other half of the shared format. Three frontends could read this file and none
could write it, which left every example of the format as prose and left
anyinfer init with no way to produce one but string templating.
Round-tripping is the contract: loads_config(dumps_config(c)) == c for every
configuration the loader accepts. What that costs is verbosity in one place — a
provider instance carrying an opt-in policy emits that policy even when every field in
it is standard, because an omitted block and a default-valued block mean different
things to the loader and only one of them is what the caller had.
Credential values are written exactly as configured. References such as env:// and
credential:// remain references, while a literal credential remains literal. This
function never resolves a reference, but callers must still review configurations that
they constructed with literal secrets before writing or committing them. Discovery and
anyinfer init produce references so their generated files contain no key material.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AnyInferConfig
|
The configuration to render. |
required |
comments
|
bool
|
Write a leading |
False
|
Returns:
| Type | Description |
|---|---|
str
|
UTF-8 JSON text, two-space indented, ending in a newline. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If a provider's |
anyinfer.dump_config ¶
dump_config(
config: AnyInferConfig,
path: str | Path,
*,
force: bool = False,
) -> None
Write a configuration to a file, refusing to replace one that exists.
Destructive-by-default is not acceptable for a file a user may have hand-tuned, and a configuration file is exactly that kind of file. Overwriting is available and has to be asked for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AnyInferConfig
|
The configuration to write. |
required |
path
|
str | Path
|
Where to write it. Parent directories must already exist. |
required |
force
|
bool
|
Replace an existing file instead of refusing. |
False
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If the path exists and |
anyinfer.config.COMMENT_KEY
module-attribute
¶
COMMENT_KEY = '_comment'
Root key carrying a human-readable note, accepted and ignored by the loader.
The format is JSON, not JSONC, so a generated file cannot explain itself in // lines
without becoming something this loader would reject. A string under this key is the
version of that idea the format can actually carry: dumps_config(..., comments=True)
writes one, and reading it back changes nothing.
Telemetry Sinks From Configuration¶
An observers block names sinks; it does not build them. Loading a configuration file has
no side effects, so nothing opens a log file until a frontend decides to observe. See
the observers block for the file format and
observability for what to do with them.
anyinfer.config.ObserverSpec
dataclass
¶
ObserverSpec(
name: str, options: Mapping[str, Any] = dict()
)
A telemetry sink named in configuration, not yet built.
Inert by design, exactly as MCPServer is: reading a configuration file must not
open a log file any more than it should spawn a subprocess. build_observers turns
these into live sinks when a frontend actually wants them.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
|
options |
Mapping[str, Any]
|
Keyword arguments for the sink's constructor. |
anyinfer.config.build_observers ¶
build_observers(
specs: Sequence[ObserverSpec],
) -> tuple[Any, ...]
Construct live telemetry sinks from configured specs.
Separate from loading so that reading a file has no side effects — a jsonl sink
opens and holds a file, and that should happen when a frontend decides to observe,
not when a config file is parsed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
specs
|
Sequence[ObserverSpec]
|
Usually |
required |
Returns:
| Type | Description |
|---|---|
tuple[Any, ...]
|
The constructed sinks, ready to pass as |
Raises:
| Type | Description |
|---|---|
ConfigError
|
A sink rejected its options, could not open its file, or its plugin failed to build. |
anyinfer.config.BUILTIN_OBSERVERS
module-attribute
¶
BUILTIN_OBSERVERS = ('logging', 'jsonl')
Sink names that need no plugin installed.
Plugin Entry Points¶
Two entry-point groups exist so a configuration file can name an extension that no
constructor call could reach — which is the sidecar's only route to one. Discovery never
raises: a broken third-party package is recorded as a PluginLoadIssue and skipped, the
same discipline anyinfer.registry.ProviderRegistry applies to provider plugins.
Credential-store plugins carry a trust delta worth reading before publishing one; see custom schemes the sidecar can reach.
anyinfer.plugins.OBSERVER_GROUP
module-attribute
¶
OBSERVER_GROUP = 'anyinfer.observers'
Entry-point group for config-nameable telemetry sinks.
anyinfer.plugins.CREDENTIAL_STORE_GROUP
module-attribute
¶
CREDENTIAL_STORE_GROUP = 'anyinfer.credential_stores'
Entry-point group for credential resolvers adding a reference scheme.
anyinfer.plugins.load_observers ¶
load_observers() -> tuple[
dict[str, Any], list[PluginLoadIssue]
]
Discover telemetry sinks published under OBSERVER_GROUP.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A |
list[PluginLoadIssue]
|
a callable that builds one from keyword options; the caller decides which by |
tuple[dict[str, Any], list[PluginLoadIssue]]
|
whether it has options to pass. |
Note
Nothing is instantiated here. A sink that opens a file should not do so merely because a package that provides it happens to be installed.
anyinfer.plugins.load_credential_stores ¶
load_credential_stores() -> tuple[
dict[str, Any], list[PluginLoadIssue]
]
Discover credential resolvers published under CREDENTIAL_STORE_GROUP.
Discovered resolvers are placed ahead of the built-ins in default_resolver, so
first refusal is exactly what makes a custom scheme work — and exactly what would let
an installed distribution interpose on the built-in ones. Any resolver claiming to
handle env:// or credential:// is therefore dropped with a scheme-reserved
issue, mirroring the id/alias-collision refusal the anyinfer.providers group has
enforced since it shipped. A plugin is trusted to add a scheme, never to redefine one.
That guard bounds interposition, not code execution: an installed package already runs arbitrary code at interpreter startup by other means, and this group's entry points are imported and instantiated like any other. What it removes is the specific case where a compromised transitive dependency silently becomes the resolver for every credential in the process without the operator having named it anywhere.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A |
list[PluginLoadIssue]
|
arguments to build the resolver; anything failing the |
tuple[dict[str, Any], list[PluginLoadIssue]]
|
protocol, or claiming a built-in scheme, is dropped with an issue rather than |
tuple[dict[str, Any], list[PluginLoadIssue]]
|
reaching the chain. |