llmwrap

package
v1.0.0-beta.144 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package llmwrap holds helpers shared across the ADR-045 Phase 1 research-graph chain components (nl_classify, route_search, execute_subqueries, assess_sufficiency, synthesize_answer).

Two scopes:

  • LLM-response shape helpers (PR 5): extracting a balanced JSON object out of a model's response (markdown fences + prose preface), and capping a string at N bytes with an ellipsis suffix for error-line readability. Used by the three LLM- wrapping components (route_search, assess_sufficiency, synthesize_answer).
  • TriplePublisher (PR 6): the graph.mutation publish surface each component uses to stamp orchestration triples on the research-pipeline loop entity (research.classify.complete, research.route.action, etc.). Required by R0-R6 rule chain — the rule engine watches ENTITY_STATES, so per-stage completion must land as entity-state triples for rules to trigger. Used by all five components (LLM-wrapping and code-only alike).

The chat-completion adapter stays per-component so each component can keep a narrowly-purposed Router/Assessor/Synthesizer interface in its handler; only the cross-cutting helpers live here.

Index

Constants

View Source
const ErrPayloadTruncateBytes = 512

ErrPayloadTruncateBytes caps the raw-payload snippet rendered in error messages. 512 keeps the structured-emit JSON's action enum, per-action arg keys, and short rationales visible even when the model wrapped its emit in long prose.

Variables

This section is empty.

Functions

func BirthLoopEntityWithTriples

func BirthLoopEntityWithTriples(ctx context.Context, pub TriplePublisher, logger StampLogger, stage, loopID, entityID string, msgType message.Type, triples []message.Triple) error

BirthLoopEntityWithTriples births the research-pipeline loop entity with a typed-origin MessageType envelope, carrying the kickoff triples atomically via create_with_triples. This is the FIRST stamp on a pipeline loop entity — the entity must be CREATED (not auto-vivified) so subsequent per-stage stamps can append onto it; a bare add_batch to a never-created entity is rejected by graph-ingest's must-exist contract and the chain stalls before any rule fires (gh#390).

Same degraded-mode + warn-on-failure contract as StampOrchestrationTriples: pub == nil logs warn and returns nil (observability-disabled), and a publish failure is logged warn and returned for handler-level metrics. The kickoff stays best-effort and non-fatal — a birth failure stalls the chain observably (no rule fires; operator sees the gap in trajectory data) rather than crashing the parent loop.

func ExtractJSON

func ExtractJSON(content string) ([]byte, error)

ExtractJSON pulls a balanced JSON object out of an LLM response. Many models wrap structured emits in ```json fences or prose preface; this helper strips fences, then walks the first balanced {...} substring while tracking JSON string-literal context (so a `}` inside a string value, e.g. a rationale field like "axes spanning {time, entity_type}", does not truncate the extraction mid-object). Backslash-escaped quotes inside strings do not toggle the string-context flag.

Returns an error if the trimmed content contains no `{`, or if the balanced-brace walker reaches end-of-input with unmatched depth.

func StampOrchestrationTriples

func StampOrchestrationTriples(ctx context.Context, pub TriplePublisher, logger StampLogger, stage, loopID string, triples []message.Triple) error

StampOrchestrationTriples is the common per-stage emission path the five research-graph components share: call the TriplePublisher with the pre-built triple batch and log warn on failure. Failure is non-fatal at the handler level — the per-stage envelope already landed in AGENT_LOOPS via the chain's KV writes, so the operator sees the chain stall in trajectory data even when triple stamping fails. Returning the error lets handler-level metrics count it; the helper does not retry beyond the publisher's built-in retry budget.

pub == nil is treated as observability-disabled (degraded mode): the helper logs warn and returns nil rather than panicking. This matches the contract NewNATSTriplePublisher establishes — nil natsclient.Client → nil publisher → degraded chain visibility.

func Truncate

func Truncate(s string, n int) string

Truncate returns s capped at n bytes with an ellipsis suffix on over-long input. Used in error-message construction so an over-long LLM response doesn't blow up log lines or trajectory captures.

gh#190: the cap is walked back to the nearest preceding rune boundary so a multi-byte UTF-8 character can't be split across the cut. Without this, an LLM response containing CJK or emoji (3- or 4-byte runes) at the byte-n boundary produced invalid UTF-8 that slog's JSON handler then rendered as garbled "�" replacement sequences. The walk is at most 3 byte-steps for valid UTF-8 input (the longest legal Go-encoded rune is 4 bytes), so the cost is negligible vs. the readability win.

Types

type StampLogger

type StampLogger interface {
	Warn(msg string, args ...any)
}

StampLogger is the subset of *slog.Logger the StampOrchestrationTriples helper needs. Defined to keep the helper unit-testable without forcing a slog handler into the test (a *slog.Logger satisfies it directly).

type TriplePublisher

type TriplePublisher interface {
	CreateEntityWithTriples(ctx context.Context, entityID string, msgType message.Type, triples []message.Triple) error
	AddTriple(ctx context.Context, triple message.Triple) error
	AddTriplesBatch(ctx context.Context, triples []message.Triple) error
}

TriplePublisher is the narrow surface the research-graph chain components use to stamp orchestration triples on the research-pipeline loop entity (e.g., research.classify.complete, research.route.action). Each per-stage completion stamps a small atomic batch sharing one Subject so graph-ingest's per-Subject CAS path preserves the rule- matching contract (R2 / R4 branches read action / sufficient triples paired with their respective complete triples — a partial write would corrupt the dispatch).

Three emission shapes:

  • CreateEntityWithTriples: BIRTH. The FIRST write to a pipeline loop entity must CREATE it with a typed-origin MessageType envelope — graph-ingest enforces must-exist on triple.add / add_batch, so a bare batch to a never-created entity is rejected ("kv: key not found") and the chain stalls before any rule fires (gh#390). The research_graph kickoff uses this; subsequent per-stage stamps append.
  • AddTriple: single-triple convenience. Available for symmetry with the agentictools.TriplePublisher pattern; the chain's per-stage stamps all batch.
  • AddTriplesBatch: atomic per-Subject APPEND onto an already-born entity. The graph-ingest handler groups triples by Subject and CAS-applies them per entity, so a batch sharing one Subject is fully atomic.

Production satisfies it with *natsClient adapter; tests substitute an in-memory recorder so the per-component handler suites don't need a live NATS connection.

func NewNATSTriplePublisher

func NewNATSTriplePublisher(client *natsclient.Client) TriplePublisher

NewNATSTriplePublisher builds a TriplePublisher backed by the graph.mutation NATS surfaces. Returns nil when client is nil — the research-graph components treat a nil publisher as "no graph emission" and log warn at handler dispatch (the chain becomes observability-only rather than fatal).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL