virtual-context 0.3.2__tar.gz → 0.3.3__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.3.2 → virtual_context-0.3.3}/PKG-INFO +1 -1
- {virtual_context-0.3.2 → virtual_context-0.3.3}/pyproject.toml +2 -1
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/__init__.py +1 -1
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/cli/main.py +181 -17
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/model_catalog.py +15 -3
- virtual_context-0.3.3/virtual_context/data/models.yaml +207 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/handlers.py +78 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/server.py +8 -1
- {virtual_context-0.3.2 → virtual_context-0.3.3}/.gitignore +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/LICENSE +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/README-draft.md +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/README.md +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/README.md.backup +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/assets/dashboard.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/assets/hero.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/models.yaml +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/REGRESSION_MAP.md +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/conftest.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/docker-compose.test.yml +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/haiku/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/haiku/conftest.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/haiku/test_compaction.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/haiku/test_retrieval.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/haiku/test_tagging.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/conftest.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/test_compactor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/test_pipeline.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/test_provider.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/ollama/test_tag_generator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/proxy/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/proxy/test_dashboard_cors.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/proxy/test_metrics.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_assembler.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_backend_integration.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_budget_enforcement.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_cli_init.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_compaction_commit_prune.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_compactor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_compactor_concurrent.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_composite_store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_config.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_context_bleed.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_conversation_identity.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_conversation_lifecycle.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_conversation_scoping.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_embedding_tag_generator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_empty_turn_skip.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_engine_integration.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_engine_lookback.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_engine_state.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_engine_sync_turns.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_enrichment.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_graph_integration.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_link_checker.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_link_query.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_link_types.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_links_sqlite.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fact_redesign.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_fill_pass.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_find_quote.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_format_agnostic.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_headless.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_history_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_idf_retrieval.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_ingest_index_integrity.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_longmemeval_auth.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_mcp_server.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_media.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_message_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_metrics_persistence.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_model_catalog.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_model_limits.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_monitor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_multi_instance.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_noop_fact_link_store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_openrouter_provider.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_paging.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_passthrough_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_presets.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_prev_context_leak.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_provider_adapters.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy_dashboard.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy_formats.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy_message_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy_session.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_proxy_streaming.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_raw_content.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_recall_all.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_registry_lifecycle.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_request_captures_persistence.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_retriever.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_rrf_scoring.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_segmenter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_semantic_search.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_sender_identity.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_session_cache.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_session_date.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_session_state.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_storage_protocols.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_store_recovery.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_store_sqlite.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_stub_turn_handling.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_supersession.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_supersession_migration.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tag_canonicalizer.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tag_consolidator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tag_generator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tag_splitter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_telemetry.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_telemetry_integration.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tool_loop.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tool_output_interceptor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tool_result_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tool_tags.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_tui.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_turn_grouping.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_turn_tag_index.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_unified_budget.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_upstream_trim.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_vcattach.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/tests/test_verb_expansion.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual-context.yaml +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual-context.yaml.example +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/cli/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/config.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/conversation_identity.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/assembler.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/compaction_pipeline.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/compactor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/composite_store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/conversation_store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/embedding_provider.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/embedding_tag_generator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/engine_utils.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/fact_query.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/fts_preprocessor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/hint_builder.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/llm_utils.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/math_utils.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/monitor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/paging_manager.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/protocols.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/provider_adapters.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/quote_search.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/retrieval_assembler.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/retrieval_scoring.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/retriever.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/search_engine.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/segmenter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/semantic_search.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tag_canonicalizer.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tag_consolidator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tag_generator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tag_scoring.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tag_splitter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tagging_pipeline.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/telemetry.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/temporal_resolver.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tool_loop.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/tool_query.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/core/turn_tag_index.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/engine.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/ingest/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/ingest/curator.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/ingest/date_resolver.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/ingest/parsers.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/ingest/supersession.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/mcp/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/mcp/server.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/model_limits.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/openclaw/virtual-context.mjs +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/patterns.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/presets/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/presets/agentic.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/presets/base.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/presets/coding.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/providers/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/providers/anthropic.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/providers/base.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/providers/generic_openai.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/providers/ollama_native.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/_envelope.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/dashboard.html +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/dashboard.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/formats.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/helpers.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/media.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/message_filter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/metrics.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/multi.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/registry.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/session_cache.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/session_state.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/state.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/favicon-16x16.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/favicon-32x32.png +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/favicon.ico +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/static/site.webmanifest +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/tool_output_interceptor.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/proxy/vcattach.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/falkordb.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/filesystem.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/helpers.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/neo4j.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/noop_fact_link_store.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/postgres.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/storage/sqlite.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/token_counter.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/app.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/chat.tcss +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/chat_provider.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/headless.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/modals/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/modals/turn_inspector.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/state.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/__init__.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/budget_bar.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/chat_view.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/input_box.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/tag_panel.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/tui/widgets/turn_list.py +0 -0
- {virtual_context-0.3.2 → virtual_context-0.3.3}/virtual_context/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: virtual-context
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "virtual-context"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "OS-style virtual memory for LLM session context management"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-or-later"
|
|
@@ -71,6 +71,7 @@ exclude = [
|
|
|
71
71
|
artifacts = [
|
|
72
72
|
"virtual_context/proxy/static/*",
|
|
73
73
|
"virtual_context/data/anthropic-tokenizer/tokenizer.json",
|
|
74
|
+
"virtual_context/data/models.yaml",
|
|
74
75
|
]
|
|
75
76
|
|
|
76
77
|
[tool.hatch.build.targets.wheel]
|
|
@@ -508,6 +508,23 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
|
|
508
508
|
"openrouter": ["qwen/qwen3-30b-a3b", "qwen/qwen3-32b", "google/gemini-2.5-flash-preview"],
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
# Context window sizes for upstream providers — used to set correct defaults
|
|
512
|
+
_UPSTREAM_CONTEXT_WINDOWS: dict[str, int] = {
|
|
513
|
+
"anthropic": 1_000_000,
|
|
514
|
+
"openai": 128_000,
|
|
515
|
+
"gemini": 1_000_000,
|
|
516
|
+
"ollama": 128_000,
|
|
517
|
+
"openrouter": 200_000,
|
|
518
|
+
"custom": 128_000,
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
# API key env var names per provider
|
|
522
|
+
_PROVIDER_KEY_ENVS: dict[str, str] = {
|
|
523
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
524
|
+
"openai": "OPENAI_API_KEY",
|
|
525
|
+
"openrouter": "OPENROUTER_API_KEY",
|
|
526
|
+
}
|
|
527
|
+
|
|
511
528
|
|
|
512
529
|
def _prompt_tagging_provider() -> tuple[str, str]:
|
|
513
530
|
provider = _prompt_choice(
|
|
@@ -542,31 +559,43 @@ def _provider_defaults(provider: str) -> tuple[str, str]:
|
|
|
542
559
|
|
|
543
560
|
def _write_instance_config(
|
|
544
561
|
base_dir: Path, label: str, provider: str, model: str, inbound_tagger_type: str,
|
|
562
|
+
*,
|
|
563
|
+
context_window: int = 200_000,
|
|
564
|
+
host: str = "127.0.0.1",
|
|
565
|
+
port: int = 5757,
|
|
566
|
+
dashboard_token: str = "",
|
|
567
|
+
tag_api_key_env: str = "",
|
|
545
568
|
) -> str:
|
|
546
569
|
"""Generate a standalone YAML config for one proxy instance.
|
|
547
570
|
|
|
548
|
-
Creates isolated storage at
|
|
571
|
+
Creates isolated storage at ``~/.virtual-context/<label>/store.db``
|
|
549
572
|
and writes config to ``<base_dir>/virtual-context-proxy-<label>.yaml``.
|
|
550
573
|
Returns the path to the written file.
|
|
551
574
|
"""
|
|
552
|
-
storage_root =
|
|
575
|
+
storage_root = str(Path.home() / ".virtual-context" / label)
|
|
553
576
|
provider_label, base_url = _provider_defaults(provider)
|
|
554
577
|
provider_block: dict = {}
|
|
555
578
|
if provider == "ollama":
|
|
556
579
|
provider_block = {provider_label: {"type": "generic_openai", "base_url": base_url + "/v1"}}
|
|
557
580
|
elif provider == "anthropic":
|
|
558
|
-
|
|
581
|
+
prov = {"type": "anthropic"}
|
|
582
|
+
if tag_api_key_env:
|
|
583
|
+
prov["api_key_env"] = tag_api_key_env
|
|
584
|
+
provider_block = {provider_label: prov}
|
|
559
585
|
elif provider == "openai":
|
|
560
|
-
|
|
586
|
+
prov = {"type": "generic_openai", "base_url": base_url}
|
|
587
|
+
if tag_api_key_env:
|
|
588
|
+
prov["api_key_env"] = tag_api_key_env
|
|
589
|
+
provider_block = {provider_label: prov}
|
|
561
590
|
elif provider == "openrouter":
|
|
562
|
-
provider_block = {provider_label: {"type": "openrouter", "base_url": base_url, "api_key_env": "OPENROUTER_API_KEY"}}
|
|
591
|
+
provider_block = {provider_label: {"type": "openrouter", "base_url": base_url, "api_key_env": tag_api_key_env or "OPENROUTER_API_KEY"}}
|
|
563
592
|
else:
|
|
564
593
|
provider_block = {provider_label: {"type": "generic_openai", "base_url": base_url}}
|
|
565
594
|
|
|
566
595
|
cfg: dict = {
|
|
567
596
|
"version": "0.2",
|
|
568
597
|
"storage_root": storage_root,
|
|
569
|
-
"context_window":
|
|
598
|
+
"context_window": context_window,
|
|
570
599
|
"token_counter": "estimate",
|
|
571
600
|
"tag_generator": {
|
|
572
601
|
"type": "llm" if provider != "keyword" else "keyword",
|
|
@@ -596,6 +625,23 @@ def _write_instance_config(
|
|
|
596
625
|
return str(out_path)
|
|
597
626
|
|
|
598
627
|
|
|
628
|
+
def _check_provider_reachable(provider: str) -> bool:
|
|
629
|
+
"""Quick check if a tagging provider is reachable."""
|
|
630
|
+
if provider == "ollama":
|
|
631
|
+
try:
|
|
632
|
+
import urllib.request
|
|
633
|
+
urllib.request.urlopen("http://127.0.0.1:11434/api/tags", timeout=3)
|
|
634
|
+
return True
|
|
635
|
+
except Exception:
|
|
636
|
+
return False
|
|
637
|
+
return True # Remote providers — can't cheaply validate without a key
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _generate_dashboard_token() -> str:
|
|
641
|
+
import secrets
|
|
642
|
+
return secrets.token_urlsafe(24)
|
|
643
|
+
|
|
644
|
+
|
|
599
645
|
def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
600
646
|
"""Interactive wizard for proxy instance setup.
|
|
601
647
|
|
|
@@ -606,13 +652,47 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
606
652
|
print("\n--- Tagging & Summarization ---")
|
|
607
653
|
tag_provider, tag_model = _prompt_tagging_provider()
|
|
608
654
|
|
|
655
|
+
# Validate tagging provider is reachable
|
|
656
|
+
if not _check_provider_reachable(tag_provider):
|
|
657
|
+
print(f"\n WARNING: {tag_provider} does not appear to be running.")
|
|
658
|
+
if tag_provider == "ollama":
|
|
659
|
+
print(" Start Ollama first: ollama serve")
|
|
660
|
+
if not _prompt_yes_no("Continue anyway?", default_yes=False):
|
|
661
|
+
sys.exit(0)
|
|
662
|
+
|
|
663
|
+
# API key for tagging provider
|
|
664
|
+
tag_api_key_env = ""
|
|
665
|
+
tag_key_env = _PROVIDER_KEY_ENVS.get(tag_provider)
|
|
666
|
+
if tag_key_env:
|
|
667
|
+
existing = os.environ.get(tag_key_env, "")
|
|
668
|
+
if existing:
|
|
669
|
+
print(f"\n Found {tag_key_env} in environment.")
|
|
670
|
+
tag_api_key_env = tag_key_env
|
|
671
|
+
else:
|
|
672
|
+
print(f"\n The {tag_provider} tagger needs an API key.")
|
|
673
|
+
print(f" Set {tag_key_env} in your environment before starting the proxy.")
|
|
674
|
+
tag_api_key_env = tag_key_env
|
|
675
|
+
|
|
609
676
|
inbound_tagger_type = _prompt_choice(
|
|
610
677
|
"Inbound tagging mode:",
|
|
611
678
|
["embedding", "llm", "keyword"],
|
|
612
679
|
default="embedding",
|
|
613
680
|
)
|
|
614
681
|
|
|
615
|
-
|
|
682
|
+
# Client selection — determines post-wizard instructions
|
|
683
|
+
client = _prompt_choice(
|
|
684
|
+
"What client will you use with this proxy?",
|
|
685
|
+
["Claude Code", "Claude Desktop", "Other / REST API"],
|
|
686
|
+
default="Claude Code",
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
# Dashboard token
|
|
690
|
+
print("\n--- Security ---")
|
|
691
|
+
dashboard_token = _generate_dashboard_token()
|
|
692
|
+
print(f" Generated dashboard token: {dashboard_token}")
|
|
693
|
+
print(" (protects dashboard mutating endpoints)")
|
|
694
|
+
|
|
695
|
+
multi = _prompt_yes_no("\nConfigure multiple proxy instances?", default_yes=False)
|
|
616
696
|
count = 1
|
|
617
697
|
if multi:
|
|
618
698
|
count = _prompt_int("How many proxy instances?", default=2, min_value=1)
|
|
@@ -620,7 +700,8 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
620
700
|
instances: list[dict] = []
|
|
621
701
|
config_paths: list[str] = []
|
|
622
702
|
used_ports: set[int] = set()
|
|
623
|
-
base_dir = Path.
|
|
703
|
+
base_dir = Path.home() / ".virtual-context"
|
|
704
|
+
base_dir.mkdir(parents=True, exist_ok=True)
|
|
624
705
|
|
|
625
706
|
for i in range(count):
|
|
626
707
|
print(f"\n--- Instance {i + 1}/{count} ---")
|
|
@@ -634,6 +715,10 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
634
715
|
label = _prompt("Label", default_label)
|
|
635
716
|
upstream = _prompt("Upstream URL", default_upstream)
|
|
636
717
|
|
|
718
|
+
# Context window — default based on upstream provider
|
|
719
|
+
default_cw = _UPSTREAM_CONTEXT_WINDOWS.get(provider, 200_000)
|
|
720
|
+
context_window = _prompt_int("Context window (tokens)", default=default_cw, min_value=8_000)
|
|
721
|
+
|
|
637
722
|
default_port = 5757 + i
|
|
638
723
|
while True:
|
|
639
724
|
port = _prompt_int("Port", default=default_port, min_value=1)
|
|
@@ -647,19 +732,26 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
647
732
|
|
|
648
733
|
# Per-instance tagger override
|
|
649
734
|
use_different_tagger = False
|
|
735
|
+
inst_tag_provider = tag_provider
|
|
736
|
+
inst_tag_model = tag_model
|
|
737
|
+
inst_tag_key_env = tag_api_key_env
|
|
650
738
|
if count > 1:
|
|
651
739
|
use_different_tagger = _prompt_yes_no(
|
|
652
740
|
f"Use a different tagger provider for '{label}'?", default_yes=False,
|
|
653
741
|
)
|
|
654
742
|
|
|
655
|
-
inst_tag_provider = tag_provider
|
|
656
|
-
inst_tag_model = tag_model
|
|
657
743
|
if use_different_tagger:
|
|
658
744
|
inst_tag_provider, inst_tag_model = _prompt_tagging_provider()
|
|
745
|
+
inst_tag_key_env = _PROVIDER_KEY_ENVS.get(inst_tag_provider, "")
|
|
659
746
|
|
|
660
747
|
# Write per-instance config
|
|
661
748
|
cfg_path = _write_instance_config(
|
|
662
749
|
base_dir, label, inst_tag_provider, inst_tag_model, inbound_tagger_type,
|
|
750
|
+
context_window=context_window,
|
|
751
|
+
host=host,
|
|
752
|
+
port=port,
|
|
753
|
+
dashboard_token=dashboard_token,
|
|
754
|
+
tag_api_key_env=inst_tag_key_env,
|
|
663
755
|
)
|
|
664
756
|
config_paths.append(cfg_path)
|
|
665
757
|
|
|
@@ -670,13 +762,16 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
670
762
|
"label": label,
|
|
671
763
|
"host": host,
|
|
672
764
|
"config": cfg_path,
|
|
765
|
+
"context_window": context_window,
|
|
766
|
+
"dashboard_token": dashboard_token,
|
|
673
767
|
}
|
|
674
768
|
)
|
|
675
769
|
|
|
676
770
|
# Review screen
|
|
677
771
|
print("\n--- Review ---")
|
|
678
772
|
for inst in instances:
|
|
679
|
-
print(f" [{inst['label']}] :{inst['port']} -> {inst['upstream']}")
|
|
773
|
+
print(f" [{inst['label']}] {inst['host']}:{inst['port']} -> {inst['upstream']}")
|
|
774
|
+
print(f" context window: {inst['context_window']:,} tokens")
|
|
680
775
|
print(f" config: {inst['config']}")
|
|
681
776
|
print()
|
|
682
777
|
|
|
@@ -684,6 +779,48 @@ def _run_instance_wizard() -> tuple[list[dict], list[str]]:
|
|
|
684
779
|
print("Aborted.")
|
|
685
780
|
sys.exit(0)
|
|
686
781
|
|
|
782
|
+
# Post-wizard summary
|
|
783
|
+
print("\n" + "=" * 60)
|
|
784
|
+
print("SETUP COMPLETE")
|
|
785
|
+
print("=" * 60)
|
|
786
|
+
vc_home = Path.home() / ".virtual-context"
|
|
787
|
+
print(f"\n Config files: {vc_home}/")
|
|
788
|
+
print(f" Storage: {vc_home}/<label>/")
|
|
789
|
+
print(f" Dashboard: http://{instances[0]['host']}:{instances[0]['port']}/dashboard")
|
|
790
|
+
|
|
791
|
+
# API key reminders
|
|
792
|
+
needed_keys = set()
|
|
793
|
+
if tag_key_env and not os.environ.get(tag_key_env, ""):
|
|
794
|
+
needed_keys.add(tag_key_env)
|
|
795
|
+
if needed_keys:
|
|
796
|
+
print("\n API keys needed (set before starting proxy):")
|
|
797
|
+
for k in sorted(needed_keys):
|
|
798
|
+
print(f" export {k}=\"your-key-here\"")
|
|
799
|
+
|
|
800
|
+
# Client-specific instructions
|
|
801
|
+
inst = instances[0]
|
|
802
|
+
if "Claude Code" in client:
|
|
803
|
+
print(f"\n Claude Code setup:")
|
|
804
|
+
print(f" Set this in your environment or .claude/settings.json:")
|
|
805
|
+
print(f" ANTHROPIC_BASE_URL=http://{inst['host']}:{inst['port']}/")
|
|
806
|
+
print(f"\n Disable auto-compact in Claude Code:")
|
|
807
|
+
print(f" Type /config -> Auto-compact -> disable")
|
|
808
|
+
print(f"\n Set VC_DASHBOARD_TOKEN in your environment:")
|
|
809
|
+
print(f" export VC_DASHBOARD_TOKEN=\"{dashboard_token}\"")
|
|
810
|
+
elif "Desktop" in client:
|
|
811
|
+
print(f"\n Claude Desktop setup:")
|
|
812
|
+
print(f" In Claude Desktop settings, set the API base URL to:")
|
|
813
|
+
print(f" http://{inst['host']}:{inst['port']}/")
|
|
814
|
+
else:
|
|
815
|
+
print(f"\n REST API / Custom client:")
|
|
816
|
+
print(f" Proxy endpoint: http://{inst['host']}:{inst['port']}/")
|
|
817
|
+
print(f" Forward your LLM API requests through this proxy.")
|
|
818
|
+
|
|
819
|
+
print(f"\n Logs:")
|
|
820
|
+
print(f" Proxy: tail -f ~/Library/Logs/virtual-context.log")
|
|
821
|
+
print(f" Errors: tail -f ~/Library/Logs/virtual-context.err.log")
|
|
822
|
+
print()
|
|
823
|
+
|
|
687
824
|
return instances, config_paths
|
|
688
825
|
|
|
689
826
|
|
|
@@ -703,7 +840,10 @@ def _proxy_command(config_path: Path, upstream: str | None) -> str:
|
|
|
703
840
|
return cmd
|
|
704
841
|
|
|
705
842
|
|
|
706
|
-
def _install_launchd_daemon(
|
|
843
|
+
def _install_launchd_daemon(
|
|
844
|
+
config_path: Path, upstream: str | None, start: bool,
|
|
845
|
+
*, dashboard_token: str = "",
|
|
846
|
+
) -> None:
|
|
707
847
|
from xml.sax.saxutils import escape as _xml_escape
|
|
708
848
|
|
|
709
849
|
launch_agents = Path.home() / "Library" / "LaunchAgents"
|
|
@@ -712,6 +852,17 @@ def _install_launchd_daemon(config_path: Path, upstream: str | None, start: bool
|
|
|
712
852
|
cmd = _proxy_command(config_path, upstream)
|
|
713
853
|
log_path = Path.home() / "Library" / "Logs" / "virtual-context.log"
|
|
714
854
|
err_path = Path.home() / "Library" / "Logs" / "virtual-context.err.log"
|
|
855
|
+
|
|
856
|
+
# Build optional EnvironmentVariables block
|
|
857
|
+
env_block = ""
|
|
858
|
+
if dashboard_token:
|
|
859
|
+
env_block = f""" <key>EnvironmentVariables</key>
|
|
860
|
+
<dict>
|
|
861
|
+
<key>VC_DASHBOARD_TOKEN</key>
|
|
862
|
+
<string>{_xml_escape(dashboard_token)}</string>
|
|
863
|
+
</dict>
|
|
864
|
+
"""
|
|
865
|
+
|
|
715
866
|
# Escape XML special characters to prevent XML injection
|
|
716
867
|
plist = f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
717
868
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -733,7 +884,7 @@ def _install_launchd_daemon(config_path: Path, upstream: str | None, start: bool
|
|
|
733
884
|
<string>{_xml_escape(str(log_path))}</string>
|
|
734
885
|
<key>StandardErrorPath</key>
|
|
735
886
|
<string>{_xml_escape(str(err_path))}</string>
|
|
736
|
-
</dict>
|
|
887
|
+
{env_block}</dict>
|
|
737
888
|
</plist>
|
|
738
889
|
"""
|
|
739
890
|
plist_path.write_text(plist)
|
|
@@ -829,7 +980,9 @@ def _install_windows_task_daemon(config_path: Path, upstream: str | None, start:
|
|
|
829
980
|
|
|
830
981
|
|
|
831
982
|
def cmd_onboard(args):
|
|
832
|
-
|
|
983
|
+
vc_home = Path.home() / ".virtual-context"
|
|
984
|
+
vc_home.mkdir(parents=True, exist_ok=True)
|
|
985
|
+
config_path = Path(args.config) if args.config else vc_home / "virtual-context.yaml"
|
|
833
986
|
|
|
834
987
|
if not config_path.exists():
|
|
835
988
|
preset = get_preset(args.preset)
|
|
@@ -863,12 +1016,20 @@ def cmd_onboard(args):
|
|
|
863
1016
|
interactive = bool(args.wizard and sys.stdin.isatty())
|
|
864
1017
|
selected_upstream = args.upstream
|
|
865
1018
|
|
|
1019
|
+
_wizard_dashboard_token = ""
|
|
866
1020
|
if interactive:
|
|
867
1021
|
instances, _config_paths = _run_instance_wizard()
|
|
868
1022
|
_apply_proxy_instances(config_path, instances)
|
|
869
1023
|
print(f"Updated {config_path} with {len(instances)} proxy instance(s).")
|
|
870
|
-
# In
|
|
871
|
-
|
|
1024
|
+
# In wizard mode, use the first instance's config for daemon install
|
|
1025
|
+
if _config_paths:
|
|
1026
|
+
config_path = Path(_config_paths[0])
|
|
1027
|
+
# Extract upstream and dashboard token from first instance
|
|
1028
|
+
if instances:
|
|
1029
|
+
selected_upstream = instances[0].get("upstream")
|
|
1030
|
+
_wizard_dashboard_token = instances[0].get("dashboard_token", "")
|
|
1031
|
+
else:
|
|
1032
|
+
selected_upstream = None
|
|
872
1033
|
if not daemon_requested:
|
|
873
1034
|
daemon_requested = _prompt_yes_no("Install daemon/service now?", default_yes=True)
|
|
874
1035
|
|
|
@@ -884,7 +1045,10 @@ def cmd_onboard(args):
|
|
|
884
1045
|
system = platform.system().lower()
|
|
885
1046
|
try:
|
|
886
1047
|
if system == "darwin":
|
|
887
|
-
_install_launchd_daemon(
|
|
1048
|
+
_install_launchd_daemon(
|
|
1049
|
+
config_path, selected_upstream, start=not args.no_start,
|
|
1050
|
+
dashboard_token=_wizard_dashboard_token,
|
|
1051
|
+
)
|
|
888
1052
|
elif system == "linux":
|
|
889
1053
|
_install_systemd_user_daemon(config_path, selected_upstream, start=not args.no_start)
|
|
890
1054
|
elif system == "windows":
|
|
@@ -30,11 +30,23 @@ class ModelCatalog:
|
|
|
30
30
|
|
|
31
31
|
@classmethod
|
|
32
32
|
def default(cls) -> ModelCatalog:
|
|
33
|
+
# Search order:
|
|
34
|
+
# 1. ~/.virtual-context/models.yaml (user override)
|
|
35
|
+
# 2. Bundled inside the package (virtual_context/data/models.yaml)
|
|
36
|
+
# 3. Project root (dev checkout)
|
|
37
|
+
user_path = os.path.join(os.path.expanduser("~"), ".virtual-context", "models.yaml")
|
|
38
|
+
if os.path.exists(user_path):
|
|
39
|
+
return cls(user_path)
|
|
40
|
+
|
|
33
41
|
here = os.path.dirname(os.path.abspath(__file__))
|
|
34
|
-
|
|
42
|
+
pkg_path = os.path.join(os.path.dirname(here), "data", "models.yaml")
|
|
43
|
+
if os.path.exists(pkg_path):
|
|
44
|
+
return cls(pkg_path)
|
|
45
|
+
|
|
46
|
+
# Dev checkout: core/ -> virtual_context/ -> project root
|
|
35
47
|
root = os.path.dirname(os.path.dirname(here))
|
|
36
|
-
|
|
37
|
-
return cls(
|
|
48
|
+
root_path = os.path.join(root, "models.yaml")
|
|
49
|
+
return cls(root_path)
|
|
38
50
|
|
|
39
51
|
def _load(self, path: str) -> None:
|
|
40
52
|
if not os.path.exists(path):
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Model pricing catalog for Virtual Context telemetry.
|
|
2
|
+
# Prices in USD per million tokens. Updated 2026-03-15.
|
|
3
|
+
|
|
4
|
+
models:
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
# Anthropic
|
|
7
|
+
# ---------------------------------------------------------------------------
|
|
8
|
+
claude-haiku-4-5-20251001:
|
|
9
|
+
provider: anthropic
|
|
10
|
+
input_per_mtok: 1.00
|
|
11
|
+
output_per_mtok: 5.00
|
|
12
|
+
context_window: 200000
|
|
13
|
+
aliases: [haiku, claude-haiku, claude-haiku-4-5]
|
|
14
|
+
|
|
15
|
+
claude-sonnet-4-5-20250929:
|
|
16
|
+
provider: anthropic
|
|
17
|
+
input_per_mtok: 3.00
|
|
18
|
+
output_per_mtok: 15.00
|
|
19
|
+
context_window: 200000
|
|
20
|
+
aliases: [claude-sonnet-4-5, claude-sonnet-4-20250514]
|
|
21
|
+
|
|
22
|
+
claude-sonnet-4-6:
|
|
23
|
+
provider: anthropic
|
|
24
|
+
input_per_mtok: 3.00
|
|
25
|
+
output_per_mtok: 15.00
|
|
26
|
+
context_window: 200000
|
|
27
|
+
aliases: [sonnet, claude-sonnet]
|
|
28
|
+
|
|
29
|
+
claude-opus-4-5:
|
|
30
|
+
provider: anthropic
|
|
31
|
+
input_per_mtok: 5.00
|
|
32
|
+
output_per_mtok: 25.00
|
|
33
|
+
context_window: 200000
|
|
34
|
+
aliases: [claude-opus-4-20250514]
|
|
35
|
+
|
|
36
|
+
claude-opus-4-6:
|
|
37
|
+
provider: anthropic
|
|
38
|
+
input_per_mtok: 5.00
|
|
39
|
+
output_per_mtok: 25.00
|
|
40
|
+
context_window: 200000
|
|
41
|
+
aliases: [opus, claude-opus]
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# OpenAI
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
gpt-5.2:
|
|
47
|
+
provider: openai
|
|
48
|
+
input_per_mtok: 1.75
|
|
49
|
+
output_per_mtok: 14.00
|
|
50
|
+
context_window: 128000
|
|
51
|
+
aliases: [gpt5.2]
|
|
52
|
+
|
|
53
|
+
gpt-5.2-pro:
|
|
54
|
+
provider: openai
|
|
55
|
+
input_per_mtok: 21.00
|
|
56
|
+
output_per_mtok: 168.00
|
|
57
|
+
context_window: 128000
|
|
58
|
+
aliases: [gpt5.2-pro]
|
|
59
|
+
|
|
60
|
+
gpt-5.3-codex:
|
|
61
|
+
provider: openai
|
|
62
|
+
input_per_mtok: 1.75
|
|
63
|
+
output_per_mtok: 14.00
|
|
64
|
+
context_window: 128000
|
|
65
|
+
aliases: [gpt5-codex, codex]
|
|
66
|
+
|
|
67
|
+
gpt-5.4:
|
|
68
|
+
provider: openai
|
|
69
|
+
input_per_mtok: 2.50
|
|
70
|
+
output_per_mtok: 15.00
|
|
71
|
+
context_window: 1050000
|
|
72
|
+
aliases: [gpt5.4]
|
|
73
|
+
|
|
74
|
+
gpt-5-mini:
|
|
75
|
+
provider: openai
|
|
76
|
+
input_per_mtok: 0.25
|
|
77
|
+
output_per_mtok: 2.00
|
|
78
|
+
context_window: 128000
|
|
79
|
+
aliases: [gpt5-mini]
|
|
80
|
+
|
|
81
|
+
gpt-5-nano:
|
|
82
|
+
provider: openai
|
|
83
|
+
input_per_mtok: 0.05
|
|
84
|
+
output_per_mtok: 0.40
|
|
85
|
+
context_window: 128000
|
|
86
|
+
aliases: [gpt5-nano]
|
|
87
|
+
|
|
88
|
+
gpt-4.1-nano:
|
|
89
|
+
provider: openai
|
|
90
|
+
input_per_mtok: 0.10
|
|
91
|
+
output_per_mtok: 0.40
|
|
92
|
+
context_window: 128000
|
|
93
|
+
aliases: [gpt4-nano]
|
|
94
|
+
|
|
95
|
+
gpt-4.1-mini:
|
|
96
|
+
provider: openai
|
|
97
|
+
input_per_mtok: 0.40
|
|
98
|
+
output_per_mtok: 1.60
|
|
99
|
+
context_window: 128000
|
|
100
|
+
aliases: [gpt4-mini]
|
|
101
|
+
|
|
102
|
+
gpt-4o:
|
|
103
|
+
provider: openai
|
|
104
|
+
input_per_mtok: 2.50
|
|
105
|
+
output_per_mtok: 10.00
|
|
106
|
+
context_window: 128000
|
|
107
|
+
aliases: [gpt4o]
|
|
108
|
+
|
|
109
|
+
gpt-4o-mini:
|
|
110
|
+
provider: openai
|
|
111
|
+
input_per_mtok: 0.15
|
|
112
|
+
output_per_mtok: 0.60
|
|
113
|
+
context_window: 128000
|
|
114
|
+
aliases: [gpt4o-mini]
|
|
115
|
+
|
|
116
|
+
gpt-4.1:
|
|
117
|
+
provider: openai
|
|
118
|
+
input_per_mtok: 2.00
|
|
119
|
+
output_per_mtok: 8.00
|
|
120
|
+
context_window: 1050000
|
|
121
|
+
aliases: [gpt4.1]
|
|
122
|
+
|
|
123
|
+
gpt-5:
|
|
124
|
+
provider: openai
|
|
125
|
+
input_per_mtok: 1.25
|
|
126
|
+
output_per_mtok: 10.00
|
|
127
|
+
context_window: 128000
|
|
128
|
+
aliases: [gpt5]
|
|
129
|
+
|
|
130
|
+
o1:
|
|
131
|
+
provider: openai
|
|
132
|
+
input_per_mtok: 15.00
|
|
133
|
+
output_per_mtok: 60.00
|
|
134
|
+
context_window: 200000
|
|
135
|
+
aliases: []
|
|
136
|
+
|
|
137
|
+
o3:
|
|
138
|
+
provider: openai
|
|
139
|
+
input_per_mtok: 2.00
|
|
140
|
+
output_per_mtok: 8.00
|
|
141
|
+
context_window: 200000
|
|
142
|
+
aliases: []
|
|
143
|
+
|
|
144
|
+
o4-mini:
|
|
145
|
+
provider: openai
|
|
146
|
+
input_per_mtok: 1.10
|
|
147
|
+
output_per_mtok: 4.40
|
|
148
|
+
context_window: 200000
|
|
149
|
+
aliases: []
|
|
150
|
+
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# Google Gemini
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
gemini-2.5-pro:
|
|
155
|
+
provider: google
|
|
156
|
+
input_per_mtok: 1.25
|
|
157
|
+
output_per_mtok: 10.00
|
|
158
|
+
context_window: 1000000
|
|
159
|
+
aliases: [gemini-pro, gemini-2.5-pro-preview-03-25]
|
|
160
|
+
|
|
161
|
+
gemini-2.5-flash:
|
|
162
|
+
provider: google
|
|
163
|
+
input_per_mtok: 0.30
|
|
164
|
+
output_per_mtok: 2.50
|
|
165
|
+
context_window: 1000000
|
|
166
|
+
aliases: [gemini-flash, gemini-2.5-flash-preview-04-17]
|
|
167
|
+
|
|
168
|
+
gemini-3.1-pro-preview:
|
|
169
|
+
provider: google
|
|
170
|
+
input_per_mtok: 2.00
|
|
171
|
+
output_per_mtok: 12.00
|
|
172
|
+
context_window: 1000000
|
|
173
|
+
aliases: [gemini-3.1-pro]
|
|
174
|
+
|
|
175
|
+
gemini-3-pro-preview:
|
|
176
|
+
provider: google
|
|
177
|
+
input_per_mtok: 2.00
|
|
178
|
+
output_per_mtok: 12.00
|
|
179
|
+
context_window: 1000000
|
|
180
|
+
aliases: [gemini-3-pro]
|
|
181
|
+
|
|
182
|
+
gemini-2.0-flash:
|
|
183
|
+
provider: google
|
|
184
|
+
input_per_mtok: 0.10
|
|
185
|
+
output_per_mtok: 0.40
|
|
186
|
+
context_window: 1000000
|
|
187
|
+
aliases: [gemini-2.0-flash-001]
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
# Xiaomi MiMo
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
mimo-v2-flash:
|
|
193
|
+
provider: xiaomi
|
|
194
|
+
input_per_mtok: 0.09
|
|
195
|
+
output_per_mtok: 0.29
|
|
196
|
+
context_window: 262000
|
|
197
|
+
aliases: [mimo, mimo-flash]
|
|
198
|
+
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
# Local / Ollama (free)
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
qwen3:4b-instruct-2507-fp16:
|
|
203
|
+
provider: ollama
|
|
204
|
+
input_per_mtok: 0.00
|
|
205
|
+
output_per_mtok: 0.00
|
|
206
|
+
context_window: 32768
|
|
207
|
+
aliases: [qwen3-4b]
|