Skip to main content

internal/infra/inference/reporter.go

internal/infra/inference · 250 lines · 11 declarations · source

Declarations

var reportPromptFile

var reportPromptFile []byte

The report prompt is a file for the same reasons the extraction prompt is, and compiled in for the same reason: a report is what a thematic question is answered from, so a prompt the filesystem can change is a path by which whoever can write next to the binary changes what this system says about somebody's memory.

source

var reportPrompt

var reportPrompt = mustLoadReportPrompt(reportPromptFile)

reportPrompt is parsed once, at startup. A panic is right: the file is embedded, so a failure is a binary that must not start rather than a runtime condition that might clear.

source

func mustLoadReportPrompt

func mustLoadReportPrompt(raw []byte) reportPromptFileShape

source

type Reporter

type Reporter struct {
config Config
client *http.Client
}

Reporter writes community reports by asking a chat model.

It writes prose and nothing more. What material goes in, what is refused on the way out, and how a context is cut when it does not fit are all in the report package — those are the decisions with a correctness argument, and they must not vary with the vendor.

source

func NewReporter

func NewReporter(config Config) *Reporter

NewReporter builds a report writer against an OpenAI-compatible endpoint.

source

method Reporter.Write

func (r *Reporter) Write(ctx context.Context, c report.Context) (report.Report, error)

Write asks the model to describe one community.

source

type reportEnvelope

type reportEnvelope struct {
Title string `json:"title"`
Summary string `json:"summary"`
Importance float32 `json:"importance"`
ImportanceReason string `json:"importance_reason"`
Findings []struct {
Summary string `json:"summary"`
Explanation string `json:"explanation"`
} `json:"findings"`
}

reportEnvelope is the reply shape the prompt asks for.

source

func decodeReport

func decodeReport(content string) (report.Report, error)

decodeReport reads the reply, locating the JSON inside whatever the model actually sent.

The same problem extraction has and the same answer: a model in JSON mode still wraps its answer in prose or a code fence often enough that a plain Unmarshal turns a good report into a failure. The object is recognised by its title, so a fragment that happens to parse is skipped rather than returned as a report with everything empty.

source

func renderContext

func renderContext(c report.Context) string

renderContext writes the material out, fenced.

The order is fixed — entities, then relations, then sub-reports — because a model given the same material in a different arrangement writes a different paragraph, and two rebuilds of one community would then differ for no reason anybody could name.

source

func materialText

func materialText(c report.Context) string

materialText is every byte of somebody else's words in this context, which is what the fence has to be absent from. Assembled separately rather than checked afterwards, because a fence chosen against half the material is a fence the other half can contain.

source

func reportSystemPrompt

func reportSystemPrompt() string

reportSystemPrompt composes the prompt in an order the file does not choose.

data_boundary is last for the reason it is last in extraction: that section's job is to be the final word before somebody else's text arrives, and a file that could reorder the sections could put it in the middle, where an instruction after it appears to supersede it.

source