forked from cleveragents/cleveragents-core
711e867112
Implemented SkeletonCompressorService for ACMS context inheritance, producing compressed context representations for propagation from parent plans to child plans. Key design decisions and implementation details: - SkeletonMetadata (frozen Pydantic model): records ratio, original_tokens, compressed_tokens, and source_decision_ids for full auditability of each compression pass. Persisted on Plan.skeleton_metadata. - SkeletonCompressorService: stateless service accepting a list of ContextFragment objects and a skeleton_ratio in [0.0, 1.0]. Fragments are sorted by relevance descending with a stable secondary sort on fragment_id to guarantee deterministic output. Token budget is original_tokens*(1-ratio); fragments are greedily selected until budget is exhausted. - Ratio semantics: 0.0 = no compression (pass-through), 1.0 = maximum compression (single top fragment only), None = default 0.3. - Integration: Plan model gains optional skeleton_metadata field exposed in as_cli_dict() under the 'skeleton' key. Service registered in DI container as skeleton_compressor_service (Singleton, stateless). - Tests: 22 BDD scenarios (features/skeleton_compressor.feature) covering ratio validation, stable ordering, metadata correctness, edge cases, and plan model integration. 6 Robot Framework smoke tests. ASV benchmark suites at 10/100/1000 fragment scales. - Documentation: docs/reference/skeleton_compressor.md with ratio table, algorithm description, metadata schema, and multi-decision plan example. ISSUES CLOSED: #194
135 lines
5.3 KiB
Gherkin
135 lines
5.3 KiB
Gherkin
Feature: Skeleton compressor
|
|
As an ACMS subsystem
|
|
I want to compress context fragments for subplan inheritance
|
|
So that child plans receive relevant context within a token budget
|
|
|
|
Background:
|
|
Given a skeleton compressor service
|
|
|
|
# --- ratio validation -------------------------------------------------
|
|
|
|
Scenario: Reject ratio below 0.0
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio -0.1
|
|
Then the compressor should raise a ValueError for invalid ratio
|
|
|
|
Scenario: Reject ratio above 1.0
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 1.5
|
|
Then the compressor should raise a ValueError for invalid ratio
|
|
|
|
Scenario: Accept ratio 0.0
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 0.0
|
|
Then all fragments should be returned unchanged
|
|
|
|
Scenario: Accept ratio 1.0
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 1.0
|
|
Then only the highest-relevance fragment should be returned
|
|
|
|
Scenario: Accept ratio at boundary 0.5
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 0.5
|
|
Then compressed tokens should be at most 500
|
|
|
|
# --- default handling -------------------------------------------------
|
|
|
|
Scenario: Default ratio applied when None
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio not specified
|
|
Then the metadata ratio should equal the default 0.3
|
|
|
|
# --- stable ordering --------------------------------------------------
|
|
|
|
Scenario: Fragments with equal relevance are ordered by id
|
|
Given three fragments with equal relevance 0.5
|
|
When I compress with skeleton_ratio 0.0
|
|
Then fragments should be ordered by fragment_id ascending
|
|
|
|
Scenario: Fragments are ordered by relevance descending
|
|
Given fragments with relevances 0.9, 0.3, and 0.7
|
|
When I compress with skeleton_ratio 0.0
|
|
Then the first fragment should have relevance 0.9
|
|
And the last fragment should have relevance 0.3
|
|
|
|
# --- metadata ----------------------------------------------------------
|
|
|
|
Scenario: Metadata records correct token counts
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 0.5
|
|
Then metadata original_tokens should be 1000
|
|
And metadata compressed_tokens should be at most 500
|
|
|
|
Scenario: Metadata records source decision IDs
|
|
Given fragments with known decision IDs
|
|
When I compress with skeleton_ratio 0.0
|
|
Then metadata should contain all source decision IDs
|
|
|
|
Scenario: Metadata ratio matches input
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 0.7
|
|
Then metadata ratio should be 0.7
|
|
|
|
# --- edge cases -------------------------------------------------------
|
|
|
|
Scenario: Empty fragment list compresses to empty
|
|
Given an empty fragment list
|
|
When I compress with skeleton_ratio 0.5
|
|
Then the result should contain zero fragments
|
|
And metadata original_tokens should be 0
|
|
And metadata compressed_tokens should equal 0
|
|
|
|
Scenario: Single fragment at ratio 0.5
|
|
Given a single fragment with 100 tokens
|
|
When I compress with skeleton_ratio 0.5
|
|
Then the result should contain one fragment
|
|
|
|
# --- argument validation ----------------------------------------------
|
|
|
|
Scenario: Reject non-list fragments argument
|
|
When I compress with a non-list fragments argument
|
|
Then the compressor should raise a TypeError
|
|
|
|
Scenario: Reject fragment with negative token count
|
|
Given a fragment with negative token count
|
|
When I compress with skeleton_ratio 0.5
|
|
Then the compressor should raise a ValueError for invalid fragment
|
|
|
|
Scenario: Reject fragment with empty id
|
|
Given a fragment with empty fragment_id
|
|
When I compress with skeleton_ratio 0.5
|
|
Then the compressor should raise a ValueError for invalid fragment
|
|
|
|
Scenario: Reject fragment with relevance out of range
|
|
Given a fragment with relevance 1.5
|
|
When I compress with skeleton_ratio 0.5
|
|
Then the compressor should raise a ValueError for invalid fragment
|
|
|
|
Scenario: Reject non-ContextFragment item in list
|
|
When I compress with a list containing a non-fragment item
|
|
Then the compressor should raise a TypeError for invalid item
|
|
|
|
Scenario: Reject non-numeric skeleton_ratio
|
|
Given context fragments with total tokens 1000
|
|
When I compress with a non-numeric skeleton_ratio
|
|
Then the compressor should raise a TypeError for invalid ratio type
|
|
|
|
Scenario: Reject skeleton metadata with compressed exceeding original
|
|
When I create skeleton metadata with compressed exceeding original
|
|
Then a validation error should be raised for compressed exceeding original
|
|
|
|
# --- compression summary (original vs compressed) ----------------------
|
|
|
|
Scenario: Compression summary stored in plan metadata
|
|
Given context fragments with total tokens 1000
|
|
When I compress with skeleton_ratio 0.6
|
|
Then compressed_tokens should be less than original_tokens
|
|
|
|
# --- skeleton_ratio integration with plan model ------------------------
|
|
|
|
Scenario: Plan model accepts skeleton_metadata
|
|
Given a skeleton metadata with ratio 0.5 and 1000 original tokens and 500 compressed
|
|
When I attach skeleton_metadata to a plan
|
|
Then the plan should expose skeleton metadata in cli dict
|