Module: Riffer::Agent::Serializer

Extended by:
Serializer
Included in:
Serializer
Defined in:
lib/riffer/agent/serializer.rb

Overview

Riffer::Agent::Serializer turns a resolved agent into a self-contained, provider-neutral data hash and back into a runnable agent. A pure module (sibling to Riffer::Agent::Run), reached most often through the Riffer::Agent#to_h / Riffer::Agent.from_h delegators.

The hash carries only data — no Procs, no class references, no tool runtime. The same hash serves two rehydration targets:

  • In-process (a monolith persisting agent definitions): pass a tool_resolver that looks tool descriptors up in a local registry and returns the real, body-bearing classes. They run on the default runtime.

  • Distributed (a receiver holding only the Riffer gem): the default resolver synthesizes body-less tool shells; inject a remote Riffer::Tools::Runtime to forward each call back to the origin.

    data = Riffer::Agent::Serializer.to_h(agent: agent) rebuilt = Riffer::Agent::Serializer.from_h(data, context: “acme”)

What does not transfer

Guardrails and the skills subsystem (backend/adapter/catalog) are not serialized; a rebuilt agent enforces no guardrails and renders no skills catalog (the skill_activate tool, if present, crosses as an ordinary tool). Secrets must not be placed in provider_options/model_options: both ride on the wire as plain data.

Defined Under Namespace

Classes: VersionError

Constant Summary collapse

SCHEMA_VERSION =

The wire format version. Bumped only on an incompatible change to the hash shape; from_h refuses any other version. See from_h for the dispatch seam that carries back-compat decoders.

1
DEFAULT_TOOL_RESOLVER =

The default tool_resolver: synthesizes a body-less tool shell from a descriptor. Its #call raises — route shells through a remote runtime.

->(descriptor) { build_tool_shell(descriptor) }

Instance Method Summary collapse

Instance Method Details

#from_h(hash, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil) ⇒ Object

Reconstructs a runnable agent from a wire hash.

hash

a Symbol-keyed wire hash (parse JSON with symbolize_names: true).

context

the rebuilt agent’s runtime context — the same value you’d pass

to +Agent.new(context:)+. It is *not* used to re-resolve serialized
config (the hash is already resolved); it is threaded into tool dispatch
and read by tools/runtimes at call time (e.g. a remote runtime keying off
<tt>context[:tenant]</tt>). Defaults to an empty context.
session

an optional Riffer::Agent::Session to seed conversation history,

forwarded verbatim to +Agent.new(session:)+. The hash carries the agent
*definition*, not its history (see "What does not transfer"); pass a
session here to resume a persisted conversation. Used as-is — the caller
owns its contents, including the system instruction message. When omitted,
a fresh session seeded with the hash's instructions is built.
tool_resolver

maps a tool descriptor to a Riffer::Tool class. Defaults

to DEFAULT_TOOL_RESOLVER (body-less shells). Pass a registry lookup to
rebuild real, in-process tools.
tool_runtime

an optional Riffer::Tools::Runtime to inject (e.g. a

remote runtime for shells). When omitted, the agent uses the configured
default (+Riffer.config.tool_runtime+).

Raises Riffer::Agent::Serializer::VersionError on an unsupported schema_version, and Riffer::ArgumentError on a malformed hash.

– : (Hash[Symbol, untyped], ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent



100
101
102
103
104
105
106
107
108
109
# File 'lib/riffer/agent/serializer.rb', line 100

def from_h(hash, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
  # Version -> decoder dispatch. Adding a +when 2+ arm (a backwards-compatible
  # decoder) is how a future breaking change keeps older dicts readable.
  case hash[:schema_version]
  when SCHEMA_VERSION
    decode_v1(hash, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
  else
    raise VersionError, "Unsupported schema_version: #{hash[:schema_version].inspect} (this Riffer supports #{SCHEMA_VERSION})"
  end
end

#from_json(json, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil) ⇒ Object

Reconstructs a runnable agent from a JSON string produced by to_json. Handles the JSON parse (with symbol keys) so callers don’t have to. See from_h for the arguments.

– : (String, ?context: Hash[Symbol, untyped]?, ?session: Riffer::Agent::Session?, ?tool_resolver: ^(Hash[Symbol, untyped]) -> singleton(Riffer::Tool), ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?) -> Riffer::Agent



126
127
128
# File 'lib/riffer/agent/serializer.rb', line 126

def from_json(json, context: nil, session: nil, tool_resolver: DEFAULT_TOOL_RESOLVER, tool_runtime: nil)
  from_h(JSON.parse(json, symbolize_names: true), context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime)
end

#to_h(agent:) ⇒ Object

Snapshots a resolved agent into a self-contained wire hash.

Reads the agent’s resolved instance state — Proc-based settings have already been evaluated against the agent’s own context, so the hash carries plain strings/data, never Procs. Tools are emitted as {name, description, parameters_schema, timeout} descriptors (the resolved agent.tools, including MCP tools and skill_activate).

agent

a resolved Riffer::Agent instance.

– : (agent: Riffer::Agent) -> Hash[Symbol, untyped]



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/riffer/agent/serializer.rb', line 58

def to_h(agent:)
  config = agent.config
  {
    schema_version: SCHEMA_VERSION,
    riffer_version: Riffer::VERSION,
    identifier: config.identifier,
    model: "#{agent.provider_name}/#{agent.model_name}",
    instructions: agent.instruction_message&.content,
    model_options: config.model_options,
    provider_options: config.provider_options,
    max_steps: encode_max_steps(config.max_steps),
    structured_output: config.structured_output&.to_json_schema(strict: false),
    tools: agent.tools.map { |tool_class| tool_descriptor(tool_class) }
  }
end

#to_json(agent:) ⇒ Object

Snapshots a resolved agent to a JSON string. Convenience over JSON.generate(to_h(agent:)).

– : (agent: Riffer::Agent) -> String



116
117
118
# File 'lib/riffer/agent/serializer.rb', line 116

def to_json(agent:)
  JSON.generate(to_h(agent: agent))
end