refactor(sdkx/knowledge): remove SemanticProcessor, expose stateless context generators#27
Merged
Merged
Conversation
…context generators
Move all LLM-driven L0/L1 summarization out of the knowledge package and
into a pair of pure helper functions. Storage operations on FSStore now
exclusively persist raw content and update search indexes; callers are
expected to drive summarization explicitly and publish results back via
the existing setters and sidecar writers.
Why
- SemanticProcessor coupled the storage layer to a worker pool, retry
policy, queue, and LLM, and emitted side effects (sidecar writes +
in-memory mutations) on every AddDocument call. This forced every
consumer into the same scheduling/observability model and made it
impossible for the platform to surface processing status, recover
durably across restarts, or back-pressure the LLM.
- The only consumer of sdkx/knowledge today is the flowcraft platform,
which already has its own job/queue/persistence stack. Pushing the
processor out lets the platform orchestrate L0/L1 with its own
status table, retries, and cache invalidation, while keeping sdkx
focused on storage + retrieval.
What
- Delete SemanticProcessor and all related machinery: workers, queue,
retries, processingTask, knowledge_queue_drops_total metric,
WithSemanticProcessor, SetSemanticProcessor, WithOnEvict.
- Drop the implicit Enqueue calls in FSStore.AddDocument and
AddDocuments; raw content writes no longer trigger LLM activity.
- Add generator.go with two stateless helpers and exported prompt
constants:
GenerateDocumentContext(ctx, llm, content) -> DocumentContext
GenerateDatasetContext(ctx, llm, summaries) -> DatasetContext
Both return partial results on error (e.g. abstract is preserved when
overview generation fails) so callers can persist what succeeded.
- Keep FSStore.SetDoc{Abstract,Overview}, SetDataset{Abstract,Overview},
WriteSidecar, WriteDatasetFile, and CachedStore.EvictDataset; their
doc comments now describe the caller-driven contract.
- Update package docs and FSStore docs to make the side-effect-free
contract explicit.
Tests
- Coverage 73.3% -> 83.1%; race build clean.
- New generator_test.go covers happy path, partial-result error
semantics, nil-LLM defense, prompt routing (asserts the exported
constants are wired to the right call), and dataset rollup distilling
the abstract from the generated overview.
- New pipeline_test.go end-to-end exercises the caller-driven flow
(Generate -> Set* -> Write sidecar/dataset file -> rebuild from disk),
concurrent fan-out across documents, regression that AddDocument no
longer issues LLM calls, sidecar cleanup on delete, and
abstractStats bookkeeping on replace/clear.
- Extend cached_store_test.go to cover previously-untested paths
(AddDocuments, ListDocuments, DatasetAbstract/Overview cache + evict,
wrong-type cache fallback for dataset-level reads, and inner-error
propagation via a new errStore stub).
- go.mod: otel metric drops to an indirect dependency now that the
queue-drop counter is gone.
Caller migration
- Replace store := NewFSStore(ws, WithSemanticProcessor(p)) with
store := NewFSStore(ws); after AddDocument, schedule the work in your
own job system and call:
docCtx, err := knowledge.GenerateDocumentContext(ctx, llm, content)
if err == nil {
store.SetDocAbstract(ds, name, docCtx.Abstract)
store.SetDocOverview(ds, name, docCtx.Overview)
_ = store.WriteSidecar(ctx, ds, name, ".abstract", docCtx.Abstract)
_ = store.WriteSidecar(ctx, ds, name, ".overview", docCtx.Overview)
cached.EvictDataset(ds)
}
- The dataset-level rollup (previously triggered automatically after
each doc) is likewise caller-controlled via GenerateDatasetContext
+ SetDataset{Abstract,Overview} + WriteDatasetFile.
Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Move all LLM-driven L0/L1 summarization out of
sdkx/knowledgeand into a pair of pure helper functions.FSStore's storage operations now exclusively persist raw content and update search indexes; callers drive summarization explicitly and publish results back via the existing setters and sidecar writers.This is a breaking change for any caller using
SemanticProcessor,WithSemanticProcessor,SetSemanticProcessor, orWithOnEvict. See migration notes below.Why
SemanticProcessorcoupled storage to a worker pool, retry policy, queue, and LLM, and emitted side effects (sidecar writes + in-memory mutations) on everyAddDocumentcall. That forced every consumer into the same scheduling/observability model and made it impossible for a higher-level platform to track processing status, recover durably across restarts, or apply its own back-pressure to the LLM.sdkx/knowledgetoday is the flowcraft platform, which already has its own job/queue/persistence stack. Pushing the processor out lets the platform orchestrate L0/L1 with its own status table, retries, and cache invalidation, while keepingsdkx/knowledgefocused on storage + retrieval.What changes
Removed
SemanticProcessorand all related machinery: workers, queue, retries,processingTask, theknowledge_queue_drops_totalmetric.WithSemanticProcessor,SetSemanticProcessor,WithOnEvictoptions.Enqueuecalls insideFSStore.AddDocumentandFSStore.AddDocuments— raw content writes no longer trigger any LLM activity.sdkx/knowledge/semantic.go(~430 lines).Added
sdkx/knowledge/generator.gowith two stateless helpers and exported prompt constants:Both helpers return partial results on error (e.g. abstract is preserved when overview generation fails) so callers can persist what succeeded.
Kept (now caller-facing)
FSStore.SetDocAbstract/SetDocOverview/SetDatasetAbstract/SetDatasetOverview— in-memory publishers; doc comments updated to describe the caller-driven contract.FSStore.WriteSidecar/WriteDatasetFile— durable sidecar writers.CachedStore.EvictDataset— cache invalidation hook for callers refreshing layered context out-of-band.Documentation
types.gonow states the side-effect-free contract explicitly.FSStorestruct doc points users at the generator helpers.Tests
go test -race ./sdkx/knowledge/...clean.generator_test.go(11 cases): happy path, partial-result error semantics, nil-LLM defense, prompt routing assertions (verifies the exported constants are wired to the right call), dataset rollup distilling the abstract from the generated overview.pipeline_test.go(12 cases): end-to-end caller-driven flow (Generate → Set* → Write sidecar/dataset file → rebuild from disk), concurrent fan-out across documents, regression thatAddDocumentno longer issues LLM calls, sidecar cleanup on delete,abstractStatsbookkeeping on replace/clear,searchAcrossDatasetsL0/L1 branches,Abstract/Overviewsidecar fallback when not yet indexed.cached_store_test.goextended (10 new cases): previously-untested paths forAddDocuments,ListDocuments,DatasetAbstract/DatasetOverviewcache + evict, wrong-type cache fallback for dataset-level reads, and inner-error propagation via a newerrStorestub.go.mod:go.opentelemetry.io/otel/metricdrops to an indirect dependency now that the queue-drop counter is gone.Caller migration
Before:
After:
The dataset-level rollup (previously auto-triggered after each doc) is likewise caller-controlled:
Test plan
go build ./sdk/... ./sdkx/... ./plugin/...go test ./sdk/... ./sdkx/...go test -race ./sdkx/knowledge/...go vet ./sdkx/...go tool cover -func): 83.1%, no 0% functions remain in the touched filesflowcraftplatform to the newsdkxtag, rewirewire_knowledge.goandoapi_handlers.AddDocumentto driveGenerateDocumentContextfrom a platform-owned worker (separate PR on the consumer repo)