ReqManagedAgents.AgentCore.Converse (ReqManagedAgents v0.1.0)

Copy Markdown View Source

The :agentcore_harness wire profile: the Bedrock Converse envelope.

  • inline_function/3 maps a Jido action (name + description + NimbleOptions schema) to a Harness inlineFunction (client-side / return-of-control) tool.
  • parse/1 folds a Converse event sequence into %{stop_reason, tool_uses, text}, accumulating streamed toolUse.input fragments and assistant text deltas.
  • resume_messages/2 assembles the STRICT resume contract: the next InvokeHarness MUST carry both the assistant toolUse message AND the user toolResult message, because Harness does not persist the partial inline-tool turn. Callers never hand-assemble this.

Reuses ReqManagedAgents.ToolSchema's JSON-Schema builder for the input schema.

Summary

Functions

Fold a Converse response's event sequence into %{stop_reason, tool_uses, text}.

Assemble the two messages for the next InvokeHarness resume turn: the assistant toolUse blocks AND the user toolResults.

Types

tool_result()

@type tool_result() :: %{
  tool_use_id: String.t(),
  text: String.t(),
  is_error: boolean()
}

Functions

inline_function(name, description, jido_schema)

@spec inline_function(String.t(), String.t(), keyword()) :: map()

parse(events)

@spec parse([map()]) :: %{
  stop_reason: String.t() | nil,
  tool_uses: [map()],
  text: String.t(),
  usage: map() | nil
}

Fold a Converse response's event sequence into %{stop_reason, tool_uses, text}.

Tool blocks are accumulated keyed by toolUseId — the source of truth, since Claude mints a fresh id per real tool call — and emitted in first-seen order. Input toolUse deltas route to whichever block is active at their contentBlockIndex (tracked per index).

Keying by id rather than contentBlockIndex makes parsing robust to a stream that reuses an index across two distinct ids (observed live: a stream emitted [{0, A}, {0, B}, {1, C}] — index 0 reused). An index-keyed fold dropped one tool and duplicated the other, producing a duplicate-toolUseId resume that Bedrock rejected; keying by id recovers both. A genuinely-reused id collapses to one block.

resume_messages(tool_uses, results)

@spec resume_messages([map()], [tool_result()]) :: [map()]

Assemble the two messages for the next InvokeHarness resume turn: the assistant toolUse blocks AND the user toolResults.

The harness does NOT persist the model's streamed assistant response into the session — sending only the toolResult makes Bedrock reject the turn with "the number of toolResult blocks ... exceeds the number of toolUse blocks of previous turn" (live-verified). So we echo the assistant toolUse back.

results must contain one entry per tool_uses entry (same length, each supplying the tool_use_id returned by that call). Bedrock rejects a turn whose assistant message carries duplicate toolUseIds ("duplicate Ids at messages.N.content"); parse/1 guarantees unique ids by keying tool blocks on toolUseId, so a well-formed tool_uses never trips this.