Imp.Adapter.Chat (Imp v0.5.0)

Copy Markdown View Source

Plain chat adapter: instructions plus field-labelled user content.

format/3 renders a signature and its inputs as a system message, one user/assistant pair per demo, the conversation history, and a final user message. Fields are labelled with [[ ## name ## ]] markers.

parse/3 accepts an Imp.Prediction, a map of output fields, or completion text. Text is split on those markers and the first section for each output field wins. A completion that does not cover every output field is a parse error rather than a partial prediction.

One signature-declared exception: signature.metadata[:text_field] names an output field that takes a completion carrying no marker at all. A native tool loop asks for a thought and tool calls, and a model that answers a step in plain text with no tool call has said something and called nothing — the plain reading of that completion, not a format failure worth a second LM call through Imp.Adapter.JSON. Remaining outputs take their declared defaults, so the signature says what an unanswered field means. The exception is narrow on purpose: the completion must carry no [[ ## field ## ]] line anywhere and must not be blank, a completion that carried native tool calls is a map rather than text and never reaches it, and a signature without that metadata parses exactly as before. Imp.Predict.ReActV2 sets it on its internal step signature; see that module.

Options to format/3: :demos, :response_instruction, :guidance, :omit_empty_request, and the renderer seams :output_renderer, :input_section_renderer, :system_renderer, :tool_result_renderer and :history_note_renderer, which let another adapter reuse this message assembly with its own dialect and let a host bound what a tool result costs in the prompt without changing what the loop records. :history_note_renderer is the one seam for saying something about a stored turn rather than re-rendering it: it is consulted for every history turn, native tool turns included, after that turn's own messages, and its text becomes one user message right after them — the next thing the model reads. A note is data about the turn (the answer was not delivered, the account's allowance ran out), not a rewrite of what happened, so the record the loop keeps is unchanged. Options outside that list are ignored; anything that is not a keyword list raises ArgumentError.

Summary

Functions

Renders a tool result as the text a reader should see, whether that reader is the model or a person looking at a host's tool card.

The words for a failed tool call, without the Error: that format_tool_result/1 puts before them: the text an MCP error result carries, or one sentence for any other reason.

Functions

format_tool_result(value)

@spec format_tool_result(term()) :: String.t()

Renders a tool result as the text a reader should see, whether that reader is the model or a person looking at a host's tool card.

Successful results format like any other value. A failed result renders as plain text instead of an Elixir term. An MCP error result is the text its tool wrote, after Error: unless that text already begins with "error", since the model is given no other sign that the call failed. Every other failure is one sentence after Error:: a JSON-RPC error is the server's message; a call that got no answer says why in words, and unless it was never sent, that it may have been carried out; a denied call says who declined it; a crashed tool names itself and its message, and one that exited says it may have been carried out; an unknown tool, a rejected argument or a rejected submit says what is wrong with the call; an atom reason is spelled out; and a structured :reason map reads as its reason and limit. Only the rendering is plain: the result the loop records keeps the whole structured error. Hosts that relay Imp tool results over a protocol boundary should use this so the same words reach the person that reached the model.

iex> Imp.Adapter.Chat.format_tool_result({:error, {:tool_denied, :post, :client_denied}})
"Error: post was not allowed; the person declined it."

tool_error_text(reason)

@spec tool_error_text(term()) :: String.t()

The words for a failed tool call, without the Error: that format_tool_result/1 puts before them: the text an MCP error result carries, or one sentence for any other reason.

iex> Imp.Adapter.Chat.tool_error_text({:unknown_tool, "frobnicate"})
"there is no tool named frobnicate."