virtual-context 0.2.8__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {virtual_context-0.2.8 → virtual_context-0.2.9}/PKG-INFO +13 -3
- {virtual_context-0.2.8 → virtual_context-0.2.9}/README.md +12 -2
- {virtual_context-0.2.8 → virtual_context-0.2.9}/pyproject.toml +1 -1
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_cli_init.py +40 -0
- virtual_context-0.2.9/tests/test_fact_redesign.py +95 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_format_agnostic.py +3 -3
- virtual_context-0.2.9/tests/test_store_recovery.py +192 -0
- virtual_context-0.2.9/virtual-context.yaml +109 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/__init__.py +1 -1
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/cli/main.py +95 -4
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/config.py +1 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/compaction_pipeline.py +8 -2
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/compactor.py +12 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/composite_store.py +9 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/conversation_store.py +1 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/protocols.py +3 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/store.py +16 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tag_generator.py +8 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/engine.py +1 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/dashboard.py +1 -1
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/formats.py +170 -22
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/handlers.py +22 -7
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/message_filter.py +185 -22
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/metrics.py +1 -1
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/server.py +59 -27
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/state.py +2 -2
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/falkordb.py +9 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/filesystem.py +9 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/neo4j.py +11 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/postgres.py +32 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/sqlite.py +83 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/types.py +2 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/.gitignore +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/LICENSE +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/assets/dashboard.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/assets/hero.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/models.yaml +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/REGRESSION_MAP.md +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/conftest.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/docker-compose.test.yml +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/haiku/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/haiku/conftest.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/haiku/test_compaction.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/haiku/test_retrieval.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/haiku/test_tagging.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/conftest.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/test_compactor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/test_pipeline.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/test_provider.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/ollama/test_tag_generator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/proxy/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/proxy/test_dashboard_cors.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/proxy/test_metrics.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_assembler.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_backend_integration.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_budget_enforcement.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_compaction_commit_prune.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_compactor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_compactor_concurrent.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_composite_store.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_config.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_context_bleed.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_conversation_identity.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_conversation_lifecycle.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_conversation_scoping.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_embedding_tag_generator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_empty_turn_skip.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_engine_integration.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_engine_lookback.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_engine_state.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_enrichment.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_graph_integration.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_link_checker.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_link_query.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_link_types.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fact_links_sqlite.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_fill_pass.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_find_quote.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_headless.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_history_filter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_idf_retrieval.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_ingest_index_integrity.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_longmemeval_auth.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_mcp_server.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_media.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_message_filter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_metrics_persistence.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_model_catalog.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_model_limits.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_monitor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_multi_instance.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_noop_fact_link_store.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_openrouter_provider.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_paging.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_passthrough_filter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_presets.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_prev_context_leak.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_provider_adapters.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy_dashboard.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy_formats.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy_message_filter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy_session.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_proxy_streaming.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_raw_content.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_recall_all.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_registry_lifecycle.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_request_captures_persistence.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_retriever.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_rrf_scoring.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_segmenter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_semantic_search.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_sender_identity.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_session_cache.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_session_date.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_storage_protocols.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_store_sqlite.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_stub_turn_handling.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_supersession.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_supersession_migration.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tag_canonicalizer.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tag_consolidator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tag_generator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tag_splitter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_telemetry.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_telemetry_integration.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tool_loop.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tool_output_interceptor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tool_result_filter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tool_tags.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_tui.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_turn_grouping.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_turn_tag_index.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_unified_budget.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_upstream_trim.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/tests/test_verb_expansion.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual-context.yaml.example +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/cli/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/conversation_identity.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/assembler.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/embedding_provider.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/embedding_tag_generator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/engine_utils.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/fact_query.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/fts_preprocessor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/hint_builder.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/llm_utils.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/math_utils.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/model_catalog.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/monitor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/paging_manager.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/provider_adapters.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/quote_search.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/retrieval_assembler.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/retrieval_scoring.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/retriever.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/search_engine.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/segmenter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/semantic_search.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tag_canonicalizer.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tag_consolidator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tag_scoring.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tag_splitter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tagging_pipeline.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/telemetry.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/temporal_resolver.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tool_loop.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/tool_query.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/core/turn_tag_index.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/ingest/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/ingest/curator.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/ingest/date_resolver.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/ingest/parsers.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/ingest/supersession.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/mcp/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/mcp/server.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/model_limits.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/openclaw/virtual-context.mjs +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/patterns.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/presets/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/presets/agentic.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/presets/base.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/presets/coding.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/providers/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/providers/anthropic.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/providers/base.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/providers/generic_openai.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/providers/ollama_native.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/_envelope.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/dashboard.html +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/helpers.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/media.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/multi.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/registry.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/session_cache.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/favicon-16x16.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/favicon-32x32.png +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/favicon.ico +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/static/site.webmanifest +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/proxy/tool_output_interceptor.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/helpers.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/storage/noop_fact_link_store.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/token_counter.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/app.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/chat.tcss +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/chat_provider.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/headless.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/modals/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/modals/turn_inspector.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/state.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/__init__.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/budget_bar.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/chat_view.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/input_box.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/tag_panel.py +0 -0
- {virtual_context-0.2.8 → virtual_context-0.2.9}/virtual_context/tui/widgets/turn_list.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: virtual-context
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: OS-style virtual memory for LLM session context management
|
|
5
5
|
Project-URL: Homepage, https://virtual-context.com
|
|
6
6
|
Project-URL: Repository, https://github.com/virtual-context/virtual-context
|
|
@@ -180,10 +180,16 @@ proxy:
|
|
|
180
180
|
**Daemon mode:** run as a background service:
|
|
181
181
|
|
|
182
182
|
```bash
|
|
183
|
-
|
|
183
|
+
# Creates config if needed, installs + starts daemon
|
|
184
|
+
virtual-context daemon install --upstream https://api.anthropic.com
|
|
185
|
+
|
|
186
|
+
# Or: guided interactive setup with daemon
|
|
187
|
+
virtual-context onboard --wizard --install-daemon
|
|
184
188
|
```
|
|
185
189
|
|
|
186
|
-
Daemon
|
|
190
|
+
Daemon lifecycle: `daemon status | start | stop | restart | uninstall`
|
|
191
|
+
|
|
192
|
+
Full setup docs (macOS `launchd`, Linux `systemd --user`, Windows Task Scheduler): [`docs/install.md`](docs/install.md)
|
|
187
193
|
|
|
188
194
|
### Python SDK
|
|
189
195
|
|
|
@@ -639,10 +645,14 @@ virtual-context transform -m "What about auth?"# tag + retrieve + assemble
|
|
|
639
645
|
virtual-context aliases list # show all tag aliases
|
|
640
646
|
virtual-context aliases suggest # auto-detect potential aliases
|
|
641
647
|
virtual-context aliases add db database # register alias manually
|
|
648
|
+
virtual-context init coding # create config from preset
|
|
649
|
+
virtual-context onboard # guided setup (create/validate config)
|
|
650
|
+
virtual-context onboard --wizard # interactive multi-instance setup
|
|
642
651
|
virtual-context proxy -u https://api.anthropic.com # single-instance proxy
|
|
643
652
|
virtual-context proxy # multi-instance (from config)
|
|
644
653
|
virtual-context presets list # list available config presets
|
|
645
654
|
virtual-context presets show coding # dump preset config as YAML
|
|
655
|
+
virtual-context daemon install # install daemon/service
|
|
646
656
|
virtual-context daemon status # service status (platform-specific)
|
|
647
657
|
virtual-context daemon start # start/enable daemon
|
|
648
658
|
virtual-context daemon stop # stop daemon
|
|
@@ -133,10 +133,16 @@ proxy:
|
|
|
133
133
|
**Daemon mode:** run as a background service:
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
|
|
136
|
+
# Creates config if needed, installs + starts daemon
|
|
137
|
+
virtual-context daemon install --upstream https://api.anthropic.com
|
|
138
|
+
|
|
139
|
+
# Or: guided interactive setup with daemon
|
|
140
|
+
virtual-context onboard --wizard --install-daemon
|
|
137
141
|
```
|
|
138
142
|
|
|
139
|
-
Daemon
|
|
143
|
+
Daemon lifecycle: `daemon status | start | stop | restart | uninstall`
|
|
144
|
+
|
|
145
|
+
Full setup docs (macOS `launchd`, Linux `systemd --user`, Windows Task Scheduler): [`docs/install.md`](docs/install.md)
|
|
140
146
|
|
|
141
147
|
### Python SDK
|
|
142
148
|
|
|
@@ -592,10 +598,14 @@ virtual-context transform -m "What about auth?"# tag + retrieve + assemble
|
|
|
592
598
|
virtual-context aliases list # show all tag aliases
|
|
593
599
|
virtual-context aliases suggest # auto-detect potential aliases
|
|
594
600
|
virtual-context aliases add db database # register alias manually
|
|
601
|
+
virtual-context init coding # create config from preset
|
|
602
|
+
virtual-context onboard # guided setup (create/validate config)
|
|
603
|
+
virtual-context onboard --wizard # interactive multi-instance setup
|
|
595
604
|
virtual-context proxy -u https://api.anthropic.com # single-instance proxy
|
|
596
605
|
virtual-context proxy # multi-instance (from config)
|
|
597
606
|
virtual-context presets list # list available config presets
|
|
598
607
|
virtual-context presets show coding # dump preset config as YAML
|
|
608
|
+
virtual-context daemon install # install daemon/service
|
|
599
609
|
virtual-context daemon status # service status (platform-specific)
|
|
600
610
|
virtual-context daemon start # start/enable daemon
|
|
601
611
|
virtual-context daemon stop # stop daemon
|
|
@@ -97,6 +97,46 @@ def test_daemon_restart_in_help(tmp_cwd):
|
|
|
97
97
|
assert "restart" in result.stdout
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
def test_daemon_not_installed_guard(tmp_cwd):
|
|
101
|
+
"""daemon commands should fail with a helpful message when no daemon is installed."""
|
|
102
|
+
env = {**__import__("os").environ, "HOME": str(tmp_cwd)}
|
|
103
|
+
for action in ("status", "start", "stop", "restart"):
|
|
104
|
+
result = subprocess.run(
|
|
105
|
+
[sys.executable, "-m", "virtual_context.cli.main", "daemon", action],
|
|
106
|
+
capture_output=True,
|
|
107
|
+
text=True,
|
|
108
|
+
env=env,
|
|
109
|
+
)
|
|
110
|
+
assert result.returncode != 0, f"daemon {action} should fail when not installed"
|
|
111
|
+
assert "not installed" in result.stdout.lower(), (
|
|
112
|
+
f"daemon {action} should mention 'not installed'"
|
|
113
|
+
)
|
|
114
|
+
assert "daemon install" in result.stdout, (
|
|
115
|
+
f"daemon {action} should suggest 'daemon install'"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_daemon_install_creates_config(tmp_cwd):
|
|
120
|
+
"""daemon install should auto-create config from agentic preset if none exists."""
|
|
121
|
+
config_path = tmp_cwd / "virtual-context.yaml"
|
|
122
|
+
assert not config_path.exists()
|
|
123
|
+
# daemon install will fail on launchctl/systemctl, but should create config first
|
|
124
|
+
result = _run_cli("daemon", "install")
|
|
125
|
+
assert config_path.exists(), "daemon install should create config"
|
|
126
|
+
assert "Created config" in result.stdout
|
|
127
|
+
content = config_path.read_text()
|
|
128
|
+
assert "tag_generator" in content
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_daemon_install_preserves_existing_config(tmp_cwd):
|
|
132
|
+
"""daemon install should not overwrite an existing config."""
|
|
133
|
+
config_path = tmp_cwd / "virtual-context.yaml"
|
|
134
|
+
config_path.write_text("custom: true\n")
|
|
135
|
+
result = _run_cli("daemon", "install")
|
|
136
|
+
assert config_path.read_text() == "custom: true\n"
|
|
137
|
+
assert "Created config" not in result.stdout
|
|
138
|
+
|
|
139
|
+
|
|
100
140
|
def test_presets_list(tmp_cwd):
|
|
101
141
|
result = _run_cli("presets", "list")
|
|
102
142
|
assert result.returncode == 0
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Tests for fact extraction redesign."""
|
|
2
|
+
from virtual_context.types import CompactorConfig
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_compactor_config_has_code_mode():
|
|
6
|
+
cc = CompactorConfig()
|
|
7
|
+
assert hasattr(cc, "code_mode")
|
|
8
|
+
assert cc.code_mode is True
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_config_loader_reads_code_mode():
|
|
12
|
+
from virtual_context.config import load_config
|
|
13
|
+
import tempfile, os, yaml
|
|
14
|
+
cfg = {"compaction": {"code_mode": False}, "context_window": 100000}
|
|
15
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
|
16
|
+
yaml.dump(cfg, f)
|
|
17
|
+
tmp_path = f.name
|
|
18
|
+
try:
|
|
19
|
+
config = load_config(tmp_path)
|
|
20
|
+
assert config.compactor.code_mode is False
|
|
21
|
+
finally:
|
|
22
|
+
os.unlink(tmp_path)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_replace_facts_for_segment_default():
|
|
26
|
+
from virtual_context.core.store import ContextStore
|
|
27
|
+
|
|
28
|
+
class MinimalStore(ContextStore):
|
|
29
|
+
"""Minimal concrete subclass that only implements abstract methods as no-ops."""
|
|
30
|
+
def store_segment(self, segment): return ""
|
|
31
|
+
def get_segment(self, ref, *, conversation_id=None): return None
|
|
32
|
+
def get_summary(self, ref, *, conversation_id=None): return None
|
|
33
|
+
def get_summaries_by_tags(self, tags, min_overlap=1, limit=10, before=None, after=None, conversation_id=None): return []
|
|
34
|
+
def search(self, query, tags=None, limit=5, conversation_id=None): return []
|
|
35
|
+
def get_all_tags(self, conversation_id=None): return []
|
|
36
|
+
def get_conversation_stats(self): return []
|
|
37
|
+
def get_tag_aliases(self, conversation_id=None): return {}
|
|
38
|
+
def set_tag_alias(self, alias, canonical, conversation_id=""): pass
|
|
39
|
+
def delete_segment(self, ref): return False
|
|
40
|
+
def cleanup(self, max_age=None, max_total_tokens=None): return 0
|
|
41
|
+
def save_tag_summary(self, tag_summary, conversation_id=""): pass
|
|
42
|
+
def get_tag_summary(self, tag, conversation_id=""): return None
|
|
43
|
+
def get_all_tag_summaries(self, *, conversation_id=None): return []
|
|
44
|
+
def search_full_text(self, query, limit=5, conversation_id=None): return []
|
|
45
|
+
def get_segments_by_tags(self, tags, min_overlap=1, limit=20, conversation_id=None): return []
|
|
46
|
+
|
|
47
|
+
store = MinimalStore()
|
|
48
|
+
deleted, inserted = store.replace_facts_for_segment("conv1", "seg1", [])
|
|
49
|
+
assert deleted == 0
|
|
50
|
+
assert inserted == 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_compaction_uses_replace_facts_for_segment():
|
|
54
|
+
"""Verify the compaction pipeline calls replace_facts_for_segment."""
|
|
55
|
+
import inspect
|
|
56
|
+
from virtual_context.core import compaction_pipeline
|
|
57
|
+
source = inspect.getsource(compaction_pipeline)
|
|
58
|
+
assert "replace_facts_for_segment" in source, \
|
|
59
|
+
"compaction_pipeline must call replace_facts_for_segment"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_code_mode_prompt_appended():
|
|
63
|
+
"""When code_mode is True, the compactor prompt should include the code mode block."""
|
|
64
|
+
from virtual_context.core.compactor import CODE_MODE_FACT_PROMPT
|
|
65
|
+
assert "Do NOT extract intermediary" in CODE_MODE_FACT_PROMPT
|
|
66
|
+
assert "conclusions, findings, discoveries" in CODE_MODE_FACT_PROMPT
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_tag_generator_accepts_code_mode():
|
|
70
|
+
from virtual_context.core.tag_generator import LLMTagGenerator
|
|
71
|
+
from virtual_context.types import TagGeneratorConfig
|
|
72
|
+
from unittest.mock import MagicMock
|
|
73
|
+
gen = LLMTagGenerator(llm_provider=MagicMock(), config=TagGeneratorConfig(), code_mode=True)
|
|
74
|
+
assert gen._code_mode is True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_build_tag_generator_passes_code_mode():
|
|
78
|
+
from virtual_context.core.tag_generator import build_tag_generator, LLMTagGenerator
|
|
79
|
+
from virtual_context.types import TagGeneratorConfig
|
|
80
|
+
from unittest.mock import MagicMock
|
|
81
|
+
config = TagGeneratorConfig(type="llm", provider="openrouter", model="test")
|
|
82
|
+
gen = build_tag_generator(config, MagicMock(), code_mode=True)
|
|
83
|
+
assert isinstance(gen, LLMTagGenerator)
|
|
84
|
+
assert gen._code_mode is True
|
|
85
|
+
gen2 = build_tag_generator(config, MagicMock())
|
|
86
|
+
assert gen2._code_mode is False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_engine_passes_code_mode_to_tag_generator():
|
|
90
|
+
import inspect
|
|
91
|
+
from virtual_context import engine
|
|
92
|
+
source = inspect.getsource(engine)
|
|
93
|
+
assert "code_mode" in source, "engine.py must reference code_mode"
|
|
94
|
+
assert "compactor.code_mode" in source or "config.compactor.code_mode" in source, \
|
|
95
|
+
"engine.py must read code_mode from compactor config"
|
|
@@ -450,7 +450,7 @@ class TestCollapseChainsCrossFormat:
|
|
|
450
450
|
fmt = detect_format(body)
|
|
451
451
|
tti = TurnTagIndex()
|
|
452
452
|
original_count = len(body["messages"])
|
|
453
|
-
result, collapsed, refs = collapse_turn_chains(
|
|
453
|
+
result, collapsed, refs, _ = collapse_turn_chains(
|
|
454
454
|
body, fmt, protected_recent_turns=3, turn_tag_index=tti,
|
|
455
455
|
store=None, conversation_id="test",
|
|
456
456
|
)
|
|
@@ -480,7 +480,7 @@ class TestCollapseChainsCrossFormat:
|
|
|
480
480
|
fmt = detect_format(body)
|
|
481
481
|
tti = TurnTagIndex()
|
|
482
482
|
original_count = len(body["input"])
|
|
483
|
-
result, collapsed, refs = collapse_turn_chains(
|
|
483
|
+
result, collapsed, refs, _ = collapse_turn_chains(
|
|
484
484
|
body, fmt, protected_recent_turns=3, turn_tag_index=tti,
|
|
485
485
|
store=None, conversation_id="test",
|
|
486
486
|
)
|
|
@@ -507,7 +507,7 @@ class TestCollapseChainsCrossFormat:
|
|
|
507
507
|
fmt = detect_format(body)
|
|
508
508
|
tti = TurnTagIndex()
|
|
509
509
|
original_count = len(body["messages"])
|
|
510
|
-
result, collapsed, refs = collapse_turn_chains(
|
|
510
|
+
result, collapsed, refs, _ = collapse_turn_chains(
|
|
511
511
|
body, fmt, protected_recent_turns=3, turn_tag_index=tti,
|
|
512
512
|
store=None, conversation_id="test",
|
|
513
513
|
)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Tests for store-backed pipeline recovery."""
|
|
2
|
+
|
|
3
|
+
from virtual_context.types import MonitorConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_monitor_config_has_store_recovery_threshold():
|
|
7
|
+
mc = MonitorConfig()
|
|
8
|
+
assert hasattr(mc, "store_recovery_threshold")
|
|
9
|
+
assert mc.store_recovery_threshold == 0.70
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _make_dummy_store():
|
|
13
|
+
"""Create a minimal concrete ContextStore for testing default method implementations."""
|
|
14
|
+
from virtual_context.core.store import ContextStore
|
|
15
|
+
|
|
16
|
+
class DummyStore(ContextStore):
|
|
17
|
+
def store_segment(self, segment): return ""
|
|
18
|
+
def get_segment(self, ref, *, conversation_id=None): return None
|
|
19
|
+
def get_summary(self, ref, *, conversation_id=None): return None
|
|
20
|
+
def get_summaries_by_tags(self, tags, min_overlap=1, limit=10, before=None, after=None, conversation_id=None): return []
|
|
21
|
+
def search(self, query, tags=None, limit=5, conversation_id=None): return []
|
|
22
|
+
def get_all_tags(self, conversation_id=None): return []
|
|
23
|
+
def get_conversation_stats(self): return []
|
|
24
|
+
def get_tag_aliases(self, conversation_id=None): return {}
|
|
25
|
+
def set_tag_alias(self, alias, canonical, conversation_id=""): pass
|
|
26
|
+
def delete_segment(self, ref): return False
|
|
27
|
+
def cleanup(self, max_age=None, max_total_tokens=None): return 0
|
|
28
|
+
def save_tag_summary(self, tag_summary, conversation_id=""): pass
|
|
29
|
+
def get_tag_summary(self, tag, conversation_id=""): return None
|
|
30
|
+
def get_all_tag_summaries(self, *, conversation_id=None): return []
|
|
31
|
+
def search_full_text(self, query, limit=5, conversation_id=None): return []
|
|
32
|
+
def get_segments_by_tags(self, tags, min_overlap=1, limit=20, conversation_id=None): return []
|
|
33
|
+
|
|
34
|
+
return DummyStore()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_get_chain_snapshots_for_conversation_abstract():
|
|
38
|
+
s = _make_dummy_store()
|
|
39
|
+
assert s.get_chain_snapshots_for_conversation("conv1") == []
|
|
40
|
+
assert s.get_chain_snapshots_for_conversation("conv1", min_turn=100) == []
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_get_tool_names_for_refs_abstract():
|
|
44
|
+
s = _make_dummy_store()
|
|
45
|
+
assert s.get_tool_names_for_refs(["ref1", "ref2"]) == []
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_collapse_turn_chains_recovers_from_store():
|
|
49
|
+
from unittest.mock import MagicMock
|
|
50
|
+
from virtual_context.proxy.message_filter import collapse_turn_chains
|
|
51
|
+
from virtual_context.proxy.formats import detect_format
|
|
52
|
+
from virtual_context.core.turn_tag_index import TurnTagIndex, TurnTagEntry
|
|
53
|
+
|
|
54
|
+
body = {
|
|
55
|
+
"system": "test",
|
|
56
|
+
"messages": [
|
|
57
|
+
{"role": "user", "content": "hello"},
|
|
58
|
+
{"role": "assistant", "content": [{"type": "text", "text": "hi"}]},
|
|
59
|
+
{"role": "user", "content": "current question"},
|
|
60
|
+
],
|
|
61
|
+
"model": "claude-opus-4-6",
|
|
62
|
+
}
|
|
63
|
+
fmt = detect_format(body)
|
|
64
|
+
tti = TurnTagIndex()
|
|
65
|
+
for i in range(100):
|
|
66
|
+
tti.entries.append(TurnTagEntry(turn_number=i, message_hash=f"hash_{i}", tags=[f"tag_{i}"]))
|
|
67
|
+
|
|
68
|
+
mock_store = MagicMock()
|
|
69
|
+
mock_store.get_chain_snapshots_for_conversation.return_value = [
|
|
70
|
+
{"ref": "chain_50_abc123", "turn_number": 50, "tool_output_refs": "tool_ref1,tool_ref2", "message_count": 4},
|
|
71
|
+
{"ref": "chain_80_def456", "turn_number": 80, "tool_output_refs": "tool_ref3", "message_count": 3},
|
|
72
|
+
]
|
|
73
|
+
mock_store.get_tool_names_for_refs.return_value = ["web_search", "Read"]
|
|
74
|
+
|
|
75
|
+
result, count, refs, recovered = collapse_turn_chains(
|
|
76
|
+
body, fmt,
|
|
77
|
+
protected_recent_turns=6,
|
|
78
|
+
turn_tag_index=tti,
|
|
79
|
+
store=mock_store,
|
|
80
|
+
conversation_id="test-conv",
|
|
81
|
+
client_truncated=True,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
mock_store.get_chain_snapshots_for_conversation.assert_called_once()
|
|
85
|
+
assert recovered == 2
|
|
86
|
+
assert "chain_50_abc123" in refs
|
|
87
|
+
assert "chain_80_def456" in refs
|
|
88
|
+
|
|
89
|
+
import json
|
|
90
|
+
result_json = json.dumps(result)
|
|
91
|
+
assert "Compacted turn 50" in result_json
|
|
92
|
+
assert "Compacted turn 80" in result_json
|
|
93
|
+
assert "vc_restore_tool" in result_json
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_fill_pass_restores_from_store_on_truncation():
|
|
97
|
+
from unittest.mock import MagicMock
|
|
98
|
+
from virtual_context.proxy.message_filter import fill_pass
|
|
99
|
+
from virtual_context.proxy.formats import detect_format
|
|
100
|
+
from virtual_context.types import AssembledContext, RetrievalResult
|
|
101
|
+
from virtual_context.core.turn_tag_index import TurnTagIndex, TurnTagEntry
|
|
102
|
+
import copy
|
|
103
|
+
|
|
104
|
+
body = {
|
|
105
|
+
"system": "test",
|
|
106
|
+
"messages": [
|
|
107
|
+
{"role": "user", "content": "recent question"},
|
|
108
|
+
{"role": "assistant", "content": [{"type": "text", "text": "recent reply"}]},
|
|
109
|
+
{"role": "user", "content": "current question"},
|
|
110
|
+
],
|
|
111
|
+
"model": "claude-opus-4-6",
|
|
112
|
+
}
|
|
113
|
+
fmt = detect_format(body)
|
|
114
|
+
|
|
115
|
+
mock_store = MagicMock()
|
|
116
|
+
mock_store.get_all_tag_summaries.return_value = []
|
|
117
|
+
mock_store.load_recent_turn_messages.return_value = [
|
|
118
|
+
(10, "older question about cooking", "I explained Italian techniques"),
|
|
119
|
+
(11, "what about baking?", "Bread baking involves..."),
|
|
120
|
+
]
|
|
121
|
+
mock_store.get_tool_outputs_for_turn.return_value = []
|
|
122
|
+
|
|
123
|
+
assembled = AssembledContext(
|
|
124
|
+
presented_segment_refs=set(),
|
|
125
|
+
presented_tags=set(),
|
|
126
|
+
tag_sections={},
|
|
127
|
+
retrieval_result=RetrievalResult(),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
tti = TurnTagIndex()
|
|
131
|
+
for i in range(50):
|
|
132
|
+
tti.entries.append(TurnTagEntry(turn_number=i, message_hash=f"hash_{i}", tags=[f"tag_{i}"]))
|
|
133
|
+
|
|
134
|
+
result_body, summaries, turns = fill_pass(
|
|
135
|
+
body=body, fmt=fmt,
|
|
136
|
+
outbound_tokens=70000, target_tokens=90000,
|
|
137
|
+
assembled=assembled, pre_filter_body=copy.deepcopy(body),
|
|
138
|
+
store=mock_store, conversation_id="test-conv",
|
|
139
|
+
summary_ratio=0.0,
|
|
140
|
+
client_truncated=True,
|
|
141
|
+
turn_tag_index=tti,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
mock_store.load_recent_turn_messages.assert_called_once()
|
|
145
|
+
assert turns >= 1
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_fill_pass_skips_tool_turns_from_store():
|
|
149
|
+
from unittest.mock import MagicMock
|
|
150
|
+
from virtual_context.proxy.message_filter import fill_pass
|
|
151
|
+
from virtual_context.proxy.formats import detect_format
|
|
152
|
+
from virtual_context.types import AssembledContext, RetrievalResult
|
|
153
|
+
from virtual_context.core.turn_tag_index import TurnTagIndex, TurnTagEntry
|
|
154
|
+
import copy, json
|
|
155
|
+
|
|
156
|
+
body = {
|
|
157
|
+
"system": "test",
|
|
158
|
+
"messages": [{"role": "user", "content": "current question"}],
|
|
159
|
+
"model": "claude-opus-4-6",
|
|
160
|
+
}
|
|
161
|
+
fmt = detect_format(body)
|
|
162
|
+
|
|
163
|
+
mock_store = MagicMock()
|
|
164
|
+
mock_store.get_all_tag_summaries.return_value = []
|
|
165
|
+
mock_store.load_recent_turn_messages.return_value = [
|
|
166
|
+
(10, "run the tests", "Here are the results"),
|
|
167
|
+
(11, "what about baking?", "Bread baking involves..."),
|
|
168
|
+
]
|
|
169
|
+
mock_store.get_tool_outputs_for_turn.side_effect = lambda cid, tn: ["ref1"] if tn == 10 else []
|
|
170
|
+
|
|
171
|
+
assembled = AssembledContext(
|
|
172
|
+
presented_segment_refs=set(), presented_tags=set(),
|
|
173
|
+
tag_sections={}, retrieval_result=RetrievalResult(),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
tti = TurnTagIndex()
|
|
177
|
+
for i in range(50):
|
|
178
|
+
tti.entries.append(TurnTagEntry(turn_number=i, message_hash=f"hash_{i}", tags=[f"tag_{i}"]))
|
|
179
|
+
|
|
180
|
+
result_body, summaries, turns = fill_pass(
|
|
181
|
+
body=body, fmt=fmt,
|
|
182
|
+
outbound_tokens=70000, target_tokens=90000,
|
|
183
|
+
assembled=assembled, pre_filter_body=copy.deepcopy(body),
|
|
184
|
+
store=mock_store, conversation_id="test-conv",
|
|
185
|
+
summary_ratio=0.0,
|
|
186
|
+
client_truncated=True,
|
|
187
|
+
turn_tag_index=tti,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
result_json = json.dumps(result_body)
|
|
191
|
+
assert "Bread baking" in result_json
|
|
192
|
+
assert "run the tests" not in result_json
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# virtual-context configuration — agentic preset
|
|
2
|
+
# Generated by: virtual-context init agentic
|
|
3
|
+
# Docs: https://github.com/virtual-context/virtual-context
|
|
4
|
+
#
|
|
5
|
+
# Balanced defaults for autonomous agent conversations across any domain.
|
|
6
|
+
# Tags emerge from conversation — no predefined domains to configure.
|
|
7
|
+
|
|
8
|
+
version: "0.2"
|
|
9
|
+
storage_root: ".virtualcontext"
|
|
10
|
+
context_window: 120000
|
|
11
|
+
token_counter: "estimate"
|
|
12
|
+
|
|
13
|
+
paging:
|
|
14
|
+
enabled: true
|
|
15
|
+
auto_promote: true
|
|
16
|
+
auto_evict: true
|
|
17
|
+
max_tool_loops: 10
|
|
18
|
+
|
|
19
|
+
tag_generator:
|
|
20
|
+
type: "llm"
|
|
21
|
+
provider: "ollama"
|
|
22
|
+
model: "qwen3:4b-instruct-2507-fp16"
|
|
23
|
+
max_tags: 10
|
|
24
|
+
min_tags: 5
|
|
25
|
+
max_tokens: 8192
|
|
26
|
+
prompt_mode: "compact"
|
|
27
|
+
context_lookback_pairs: 5
|
|
28
|
+
context_bleed_threshold: 0.1
|
|
29
|
+
keyword_fallback:
|
|
30
|
+
tag_keywords:
|
|
31
|
+
legal: [court, filing, motion, attorney, judge, case]
|
|
32
|
+
medical: [medication, doctor, lab, symptom, dose, blood]
|
|
33
|
+
code: [function, bug, deploy, git, API, database, error]
|
|
34
|
+
tag_patterns:
|
|
35
|
+
legal:
|
|
36
|
+
- "\\b\\d{2}-cv-\\d+\\b"
|
|
37
|
+
- "\\bN\\.?J\\.?S\\.?A\\.?\\b"
|
|
38
|
+
code:
|
|
39
|
+
- "\\bdef \\w+\\("
|
|
40
|
+
- "\\bclass \\w+[:(]"
|
|
41
|
+
|
|
42
|
+
tag_rules:
|
|
43
|
+
- match: "legal*"
|
|
44
|
+
priority: 9
|
|
45
|
+
ttl_days: null
|
|
46
|
+
- match: "medical*"
|
|
47
|
+
priority: 8
|
|
48
|
+
ttl_days: 90
|
|
49
|
+
- match: "debug*"
|
|
50
|
+
priority: 7
|
|
51
|
+
ttl_days: 7
|
|
52
|
+
- match: "*"
|
|
53
|
+
priority: 5
|
|
54
|
+
ttl_days: 30
|
|
55
|
+
|
|
56
|
+
compaction:
|
|
57
|
+
soft_threshold: 0.70
|
|
58
|
+
hard_threshold: 0.85
|
|
59
|
+
protected_recent_turns: 6
|
|
60
|
+
overflow_buffer: 1.2
|
|
61
|
+
summary_ratio: 0.15
|
|
62
|
+
min_summary_tokens: 200
|
|
63
|
+
max_summary_tokens: 2000
|
|
64
|
+
max_concurrent_summaries: 4
|
|
65
|
+
llm_token_overhead: 2000
|
|
66
|
+
|
|
67
|
+
summarization:
|
|
68
|
+
provider: "ollama"
|
|
69
|
+
model: "qwen3:4b-instruct-2507-fp16"
|
|
70
|
+
max_tokens: 1000
|
|
71
|
+
temperature: 0.3
|
|
72
|
+
|
|
73
|
+
providers:
|
|
74
|
+
ollama:
|
|
75
|
+
type: "generic_openai"
|
|
76
|
+
base_url: "http://127.0.0.1:11434/v1"
|
|
77
|
+
|
|
78
|
+
storage:
|
|
79
|
+
backend: "sqlite"
|
|
80
|
+
sqlite:
|
|
81
|
+
path: ".virtualcontext/store.db"
|
|
82
|
+
|
|
83
|
+
facts:
|
|
84
|
+
graph_links: true
|
|
85
|
+
|
|
86
|
+
assembly:
|
|
87
|
+
core_context_max_tokens: 18000
|
|
88
|
+
tag_context_max_tokens: 30000
|
|
89
|
+
facts_max_tokens: 20000
|
|
90
|
+
context_hint_enabled: true
|
|
91
|
+
context_hint_max_tokens: 2000
|
|
92
|
+
core_files: []
|
|
93
|
+
|
|
94
|
+
retrieval:
|
|
95
|
+
skip_active_tags: true
|
|
96
|
+
active_tag_lookback: 4
|
|
97
|
+
inbound_tagger_type: "embedding"
|
|
98
|
+
embedding_model: "all-MiniLM-L6-v2"
|
|
99
|
+
embedding_threshold: 0.3
|
|
100
|
+
strategy_config:
|
|
101
|
+
default:
|
|
102
|
+
min_overlap: 1
|
|
103
|
+
max_results: 10
|
|
104
|
+
max_budget_fraction: 0.25
|
|
105
|
+
include_related: true
|
|
106
|
+
|
|
107
|
+
telemetry:
|
|
108
|
+
enabled: true
|
|
109
|
+
models_file: "models.yaml"
|