diff --git a/.claude/mcp.json b/.claude/mcp.json deleted file mode 100644 index f6ddd42c..00000000 --- a/.claude/mcp.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "mcpServers": { - "codeweaver": { - "url": "http://127.0.0.1:9328", - "headers": {}, - "timeout": 120, - "description": "CodeWeaver advanced code search and understanding server.", - "icon": "https://raw.githubusercontent.com/knitli/codeweaver/main/docs/assets/codeweaver-primary.svg" - } - } -} diff --git a/.claude/mcp.json.license b/.claude/mcp.json.license deleted file mode 100644 index 3a9c57d7..00000000 --- a/.claude/mcp.json.license +++ /dev/null @@ -1,4 +0,0 @@ -SPDX-FileCopyrightText: 2025 Knitli Inc. -SPDX-FileContributor: Adam Poulemanos - -SPDX-License-Identifier: MIT OR Apache-2.0 diff --git a/.mcp.json b/.mcp.json index f54caf66..2a8400b6 100644 --- a/.mcp.json +++ b/.mcp.json @@ -3,59 +3,6 @@ "codeweaver": { "type": "http", "url": "http://127.0.0.1:9328/mcp/" - }, - "context7": { - "args": [ - "-y", - "@upstash/context7-mcp@latest" - ], - "command": "npx", - "type": "stdio" - }, - "mise": { - "args": [ - "mcp" - ], - "command": "mise", - "env": { - "MISE_EXPERIMENTAL": "1", - "MISE_GITHUB_TOKEN": "${env:MISE_GITHUB_TOKEN}" - }, - "type": "stdio" - }, - "morph-llm-fast-apply": { - "args": [ - "-y", - "@morph-llm/morph-fast-apply@latest" - ], - "command": "npx", - "env": { - "ALL_TOOLS": "false", - "MORPH_API_KEY": "${env:MORPH_API_KEY}" - }, - "type": "stdio" - }, - "serena": { - "args": [ - "run", - "serena", - "start-mcp-server", - "--context", - "agent" - ], - "command": "uv", - "type": "stdio" - }, - "tavily": { - "args": [ - "-y", - "tavily-mcp@latest" - ], - "command": "npx", - "env": { - "TAVILY_API_KEY": "${env:TAVILY_API_KEY}" - }, - "type": "stdio" } } -} +} \ No newline at end of file diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 213f0db2..db02fe8e 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -1,48 +1,16 @@ { - "servers": { - "codeweaver": { - "url": "http://127.0.0.1:9328/mcp/", - "type": "http" - }, - "context7": { - "args": [ - "--bun", - "@upstash/context7-mcp@latest" - ], - "command": "bunx", - "type": "stdio" - }, - "morphLlmFastApply": { - "args": [ - "--bun", - "@morph-llm/morph-fast-apply@latest" - ], - "command": "bunx", - "env": { - "ALL_TOOLS": "false", - "MORPH_API_KEY": "${env:MORPH_API_KEY}" - }, - "type": "stdio" - }, - "serena": { - "args": [ - "run", - "serena", - "start-mcp-server", - "--context", - "agent" - ], - "command": "uv" - }, - "tavily": { - "args": [ - "--bun", - "tavily-mcp@latest" - ], - "command": "bunx", - "env": { - "TAVILY_API_KEY": "${env:TAVILY_API_KEY}" - } - } - } -} + "servers": { + "codeweaver": { + "url": "http://127.0.0.1:9328/mcp/", + "type": "http" + }, + "context7": { + "args": [ + "--bun", + "@upstash/context7-mcp@latest" + ], + "command": "bunx", + "type": "stdio" + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 2d11f911..4161dd3f 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ SPDX-FileContributor: Adam Poulemanos SPDX-License-Identifier: MIT OR Apache-2.0 --> - +
@@ -15,10 +13,9 @@ mcp-name: com.knitli/codeweaver CodeWeaver logo - # CodeWeaver -### The missing abstraction layer between AI and your code +### Semantic code search for Claude β€” across 166+ languages [![Python Version][badge_python]][link_python] [![License][badge_license]][link_license] @@ -35,38 +32,56 @@ mcp-name: com.knitli/codeweaver --- -## 🎯 What is CodeWeaver? +## What It Does -**CodeWeaver gives both humans and AI a deep, structural understanding of your project** β€” not just text search, but real context: symbols, blocks, relationships, intent. [MCP][mcp] is just the delivery mechanism; CodeWeaver is the capability. +**CodeWeaver gives Claude precise context from your codebase.** Not keyword grep. Not whole-file dumps. Actual structural understanding through hybrid semantic search. -**If you want AI that actually knows your code instead of guessing, this is the foundation.** +Ask Claude questions like: +- *"Where do we handle OAuth tokens?"* +- *"Find all API endpoint definitions"* +- *"Show me error handling in the payment flow"* -> ⚠️ **Alpha Release**: CodeWeaver is in active development. [Use it, break it, shape it, help make it better][issues]. +CodeWeaver returns the exact functions, classes, and code blocks β€” even in unfamiliar languages or massive repositories. ---- +**Example:** +``` +Without CodeWeaver: + Claude: "Let me search for 'auth'... here are 50 files mentioning authentication" + Result: Generic code, wrong context, wasted tokens + +With CodeWeaver: + You: "Where do we validate OAuth tokens?" + Claude gets: The exact 3 functions across 2 files, with surrounding context + Result: Precise answers, focused context, actual understanding +``` -## πŸ” Why CodeWeaver Exists +> ⚠️ **Alpha Release**: This works, but it's early. [Use it, break it, help shape it][issues]. -### The Problems +--- -| Problem | Impact | -|---------|--------| -| πŸ”΄ **Poor Context = Poor Results** | Agents are better at generating new code than understanding existing structure | -| πŸ’Έ **Massive Inefficiency** | Agents read the same huge files repeatedly (50%+ context waste is common) | -| πŸ”§ **Wrong Abstraction** | Tools built for humans, not for how agents actually work | -| πŸ”’ **No Ownership** | Existing solutions locked into specific IDEs or agent clients like Claude Code | +## How CodeWeaver Stacks Up -**The result**: Shallow, inconsistent, fragile context. And you don't control it. +### Quick Reference Matrix -### CodeWeaver's Approach +| Feature | CodeWeaver | Serena | Cursor | Copilot Workspace | Sourcegraph Cody | Continue.dev | Bloop | Aider | +|---------|-----------|--------|--------|-------------------|------------------|--------------|-------|-------| +| **Approach** | Semantic search | Symbol lookup (LSP) | Semantic | Semantic | Keyword | Semantic | Semantic | Repo maps | +| **Tool Count** | **1** | **20+** | N/A | N/A | N/A | N/A | N/A | N/A | +| **Prompt Overhead** | **~500 tokens** | **~16,000 tokens** | N/A | N/A | N/A | N/A | N/A | N/A | +| **Search Speed** | Moderate (embeddings) | **Very fast (LSP)** | Moderate | Server-side | Fast | Moderate | Fast | On-demand | +| **Embedding Providers** | **17** | 0 (no embeddings) | 1-2 | 1 | 0 (deprecated) | 4-5 | 1 | 0 | +| **Language Support** | **166+** | ~16 (LSP required) | ~50-100 | All (text) | All | ~165 | Unknown | ~165+ | +| **Requires Language Server** | ❌ No | βœ… Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | +| **Symbol Precision** | ⚠️ Semantic match | **βœ… Exact symbols** | ⚠️ Semantic | ⚠️ Semantic | ⚠️ Keyword | ⚠️ Semantic | ⚠️ Semantic | βœ… Exact | +| **Concept Search** | **βœ… Yes** | ❌ Symbols only | βœ… Yes | βœ… Yes | ⚠️ Limited | βœ… Yes | βœ… Yes | ❌ No | +| **Editing Capabilities** | ❌ No | **βœ… Yes (9 tools)** | βœ… Yes | βœ… Yes | βœ… Yes | βœ… Yes | ❌ No | βœ… Yes | -βœ… **One focused capability**: Structural + semantic code understanding -βœ… **Hybrid search built for code**, not text -βœ… **Works offline, airgapped, or degraded** -βœ… **Deploy it however you want** -βœ… **One great tool instead of 30 mediocre ones** +**Notes**: +- **Serena tool count**: Varies by context (20+ in claude-code, up to 35 total available) +- **Serena prompt overhead**: Measured with 21 active tools in claude-code context (~16,000 tokens) +- **Language counts**: CodeWeaver supports 166+ unique languages (27 with AST parsing, 139 with intelligent delimiter-based chunking) -πŸ“– [Read the detailed rationale β†’][why_codeweaver] +πŸ“Š [See detailed competitive analysis β†’][competitive_analysis] --- @@ -75,7 +90,6 @@ mcp-name: com.knitli/codeweaver ### Quick Install Using the [CLI](#cli) with [uv][uv_tool]: - ```bash # Add CodeWeaver to your project uv add --prerelease allow --dev code-weaver @@ -90,41 +104,42 @@ cw doctor cw server ``` -> **πŸ“ Note**: `cw init` defaults to CodeWeaver's `recommended` profile, which requires: +> **πŸ“ Note**: `cw init` defaults to CodeWeaver's `recommended` profile: > - πŸ”‘ [Voyage AI API key][voyage_ai] (generous free tier) -> - πŸ—„οΈ [Qdrant instance][qdrant] (cloud or local, generous free tier for cloud, free local) +> - πŸ—„οΈ [Qdrant instance][qdrant] (cloud or local, both free options) +> +> **Want full offline?** Use `cw init --profile quickstart` for local-only operation. 🐳 **Prefer Docker?** [See Docker setup guide β†’][docker_guide] ### MCP Configuration -CodeWeaver uses **stdio transport by default**, which proxies to the HTTP backend daemon. First start the daemon with `codeweaver start`, then MCP clients can connect via stdio. +To watch and handle your files, CodeWeaver always runs an HTTP server. You can connect to that or use your typical `stdio` setup: -`cw init` will add CodeWeaver to your project's `.mcp.json`: - -```json "with stdio (default):" +`cw init` adds CodeWeaver to your project's `.mcp.json`: +```json { "mcpServers": { "codeweaver": { "type": "stdio", "cmd": "uv", "args": ["run", "codeweaver", "server"], - "env": {"SOME_API_KEY_FOR_PROVIDERS": "value"} + "env": {"VOYAGE_API_KEY": "your-key-here"} } } } ``` -```json "with http (direct connection):" +**or with http:** +```json { "mcpServers": { "codeweaver": { "type": "http", - "url": "http://127.0.0.1:9328/mcp" + "url": "http://127.0.0.1:9328" } } } - ``` --- @@ -135,19 +150,20 @@ CodeWeaver uses **stdio transport by default**, which proxies to the HTTP backen -### 🧠 Smart Search -- **Hybrid search** (sparse + dense) -- **AST-level understanding** +### πŸ” Smart Search +- **Hybrid search** (sparse + dense vectors) +- **AST-level understanding** (27 languages) - **Semantic relationships** -- **Context-aware chunking** +- **Language-aware chunking** (166+ languages) ### 🌐 Language Support -- **26 languages** with full AST/semantic -- **166+ languages** with intelligent chunking -- **Family heuristics** for smart parsing +- **27 languages** with full AST/semantic parsing +- **166+ languages** with language-aware chunking +- **Cross-language normalization** +- **Family heuristics** for smart fallback @@ -155,39 +171,40 @@ CodeWeaver uses **stdio transport by default**, which proxies to the HTTP backen ### πŸ”„ Resilient & Offline -- **Automatic fallback** to local models -- **Works offline/airgapped** -- **Health monitoring** with graceful degradation -- **Better degraded than others' primary mode** +- **Full offline operation** with local models +- **Automatic failover** to backup vector store +- **Works airgapped** (no cloud required) +- **Graceful degradation** with health monitoring -### βš™οΈ Flexible Configuration -- **~15 config sources** (TOML/YAML/JSON) -- **Cloud secret stores** (AWS/Azure/GCP) -- **Hierarchical merging** -- **Environment overrides** +### πŸ”Œ Provider Flexibility +- **17 embedding providers** +- **50+ embedding models** +- **Sparse & dense** embedding model support +- **5 reranking providers** +- [See full provider list β†’][providers_list] -### πŸ”Œ Provider Support -- **Multiple embedding providers** -- **Sparse & dense models** -- **Reranking support** -- [See full provider list β†’][providers_list] +### βš™οΈ Configuration +- **~15 config sources** (TOML/YAML/JSON/ENV) +- **Cloud secret stores** (AWS/Azure/GCP) +- **Hierarchical merging** +- **Profiles** for common setups ### πŸ› οΈ Developer Experience - **Live indexing** with file watching -- **Low CPU overhead** +- **Move detection** (no re-indexing duplicates) - **Full CLI** (`cw` / `codeweaver`) -- **Health, metrics, status endpoints** +- **Health & metrics** endpoints @@ -195,212 +212,24 @@ CodeWeaver uses **stdio transport by default**, which proxies to the HTTP backen --- -## πŸ—οΈ How It Works - -CodeWeaver combines [AST][wiki_ast]-level understanding, semantic relationships, and hybrid embeddings (sparse + dense) to deliver both contextual and literal understanding of your codebase. - -**The goal: give AI the fragments it *should* see, not whatever it can grab.** - -### Architecture Highlights - -```text -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Your Codebase β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Live Indexing β”‚ ← AST parsing + semantic analysis - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Hybrid Vector Store β”‚ ← Sparse + Dense embeddings - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Reranking Layer β”‚ ← Relevance optimization (heuristic and reranking model) - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ MCP Interface β”‚ ← Simple "find_code" tool (`find_code("authentication api")`) - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ AI β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -### CLI Commands - -```bash -cw start # Start daemon in background (or --foreground) -cw stop # Stop the daemon -cw server # Run the MCP server (stdio by default) -cw doctor # Full setup diagnostic -cw index # Run indexing without server -cw init # Set up MCP + config -cw list # List providers, models, capabilities -cw status # Live server status, health, index state -cw search # Test the search engine -cw config # View resolved configuration -``` - -#### Running as a System Service - -Install CodeWeaver to start automatically on login: - -```bash -cw init service # Install and enable (systemd/launchd) -cw init service --uninstall # Remove the service -``` - -πŸ“– [Full CLI Guide β†’][cli_guide] - - ---- - -## πŸ“Š Current Status (Alpha) - -### Stability Snapshot: Strong Core, Prickly Edges - -| Component | Status | Notes | -|-----------|--------|-------| -| πŸ”„ **Live indexing & file watching** | ⭐⭐⭐⭐ | Runs continuously; reliable | -| 🌳 **AST-based chunking** | ⭐⭐⭐⭐ | Full semantic/AST for 26 languages | -| πŸ“ **Context-aware chunking** | ⭐⭐⭐⭐ | 166+ languages, heuristic AST-lite | -| πŸ”Œ **Provider integration** | ⭐⭐⭐ | Voyage/FastEmbed reliable, others vary | -| πŸ›‘οΈ **Automatic fallback** | ⭐⭐⭐ | Seamless offline/degraded mode | -| πŸ’» **CLI** | ⭐⭐⭐⭐ | Core commands fully wired and tested | -| 🐳 **Docker build** | ⭐⭐⭐ | Skip local Qdrant setup entirely | -| πŸ”— **MCP interface** | ⭐⭐⭐ | Core ops reliable, some edge cases | -| 🌐 **HTTP endpoints** | ⭐⭐⭐ | Health, metrics, state, versions stable | - -_Legend: ⭐⭐⭐⭐ = solid | ⭐⭐⭐ = works with quirks | ⭐⭐ = experimental | ⭐ = chaos gremlin_ - ---- - -## πŸ—ΊοΈ Roadmap - -The [`enhancement`][enhancement_label] issues describe detailed plans. Short version: - -- πŸ“š **Way better docs** – comprehensive guides and tutorials -- πŸ€– **AI-powered context curation** – agents identify purpose and intent -- πŸ”§ **Data provider integration** – Tavily, DuckDuckGo, Context7, and more -- πŸ’‰ **True DI system** – replace existing registry -- πŸ•ΈοΈ **Advanced orchestration** – integrate `pydantic-graph` - -### What Will Stay: **One Tool** - -**One tool**. We give AI agents one simple tool: `find_code`. - -Agents just need to explain what they need. No complex schemas. No novella-length prompts. - ---- - -## πŸ“š Documentation - -### For Users -- 🐳 [Docker Setup Notes][docker_notes] -- πŸš€ [Getting Started Guide][nav_install] - -### For Developers -- πŸ—οΈ [Overall Architecture][architecture] -- πŸ” [find_code API][api_find_code] -- πŸ“ [find_code Architecture][arch_find_code] - -### Product Philosophy -- πŸ’­ [Product Decisions][product_decisions] – transparency matters -- πŸ€” [Why CodeWeaver?][why_codeweaver] – detailed rationale - - - ---- - -## 🀝 Contributing - -**PRs, issues, weird edge cases, feature requests β€” all welcome!** - -This is still early, and the best time to help shape the direction. +## πŸ’­ Philosophy -### How to Contribute +### The Bigger Picture -1. 🍴 Fork the repository -2. 🌿 Create a feature branch -3. ✨ Make your changes -4. βœ… Add tests if applicable -5. πŸ“ Update documentation -6. πŸš€ Submit a PR +I started building CodeWeaver because I believe AI agents need better context infrastructure. Right now: -You'll need to agree to our [Contributor License Agreement][cla]. +- Agents re-read the same huge files repeatedly +- They get shallow, text-based context instead of structural understanding +- They are mostly given tools built for humans, not for how they actually work +- You don't control what context they see or how they get it -### Found a Bug? - -πŸ› [Report it here][issues] – include as much detail as possible! - ---- +CodeWeaver addresses this with one focused capability: structural + semantic code understanding that you control and can deploy however you want. -## πŸ”— Links +**Is this solving a big problem?** We think so. But we're in alpha; we're probably not there yet. We also need real-world usage to prove it. That's where you come in. Use it, make it better. Worst case -- it's a good tool, best case -- you get better results and cut costs on AI. -### Project -- πŸ“¦ **Repository**: [github.com/knitli/codeweaver][repo] -- πŸ› **Issues**: [Report bugs & request features][issues] -- πŸ“‹ **Changelog**: [View release history][changelog] - - -### Company -- 🏒 **Knitli**: [knitli.com][knitli_site] -- ✍️ **Blog**: [blog.knitli.com][knitli_blog] -- 🐦 **X/Twitter**: [@knitli_inc][knitli_x] -- πŸ’Ό **LinkedIn**: [company/knitli][knitli_linkedin] -- πŸ’» **GitHub**: [@knitli][knitli_github] - -### Support the Project - -We're a [one-person company][bashandbone] at the moment... and make no money... if you like CodeWeaver and want to keep it going, please consider **[sponsoring me][sponsor]** πŸ˜„ - ---- - -## πŸ“¦ Package Info - -- **Python package**: `code-weaver` πŸ‘ˆβ— **note the hyphen** -- **CLI commands**: `cw` / `codeweaver` -- **Python requirement**: β‰₯3.12 (tested on 3.12, 3.13, 3.14) -- **Entry point**: `codeweaver.cli.app:main` - ---- - -## πŸ“„ License - -Licensed under **MIT OR Apache 2.0** β€” you choose! Some vendored code is Apache 2.0 only and some is MIT only. Everything is permissively licensed. - -The project follows the [REUSE specification][reuse_spec]. Every file has detailed licensing information, and we regularly generate a [software bill of materials][sbom]. - ---- - -## πŸ“Š Telemetry - -The default includes **very anonymized telemetry** to improve CodeWeaver. [See the implementation][telemetry_impl] or read [the README][telemetry_readme]. - -**Opt out**: `export CODEWEAVER__TELEMETRY__DISABLE_TELEMETRY=true` - -**Opt in to detailed feedback** (helps us improve): `export CODEWEAVER__TELEMETRY__TOOLS_OVER_PRIVACY=true` - -πŸ“‹ [See our privacy policy][privacy_policy] - ---- - -## ⚠️ API Stability - -> **Warning**: The API *will change*. Our priority right now is giving you and your coding agent an awesome tool. -> -> To deliver on that, we can't get locked into API contracts while we're in alpha. We also want you to be able to extend and build on CodeWeaver β€” once we get to stable releases. +πŸ“– [Read the detailed rationale β†’][why_codeweaver] --- -
**Built with ❀️ by [Knitli][knitli_site]** @@ -422,6 +251,7 @@ The default includes **very anonymized telemetry** to improve CodeWeaver. [See t [arch_find_code]: "find_code Architecture" [architecture]: "Overall Architecture" [bashandbone]: "Adam Poulemanos' GitHub Profile" +[competitive_analysis]: "See how CodeWeaver stacks up" [changelog]: "Changelog" [cla]: "Contributor License Agreement" [cli_guide]: "Command Line Reference" diff --git a/claudedocs/cli-ux-improvements-summary.md b/claudedocs/cli-ux-improvements-summary.md deleted file mode 100644 index 1e8e0234..00000000 --- a/claudedocs/cli-ux-improvements-summary.md +++ /dev/null @@ -1,298 +0,0 @@ - - -# CLI UX Improvements Summary - -## Overview - -This document summarizes the CLI user experience improvements made to the Option A service architecture implementation plan based on UX analysis. - -## Key Changes - -### 1. Background Services Management - -**New Commands:** -- `cw start` - Start background services (indexing, file watching, telemetry, management server) -- `cw stop` - Stop background services gracefully - -**Rationale:** -- Simple, intuitive interface ("start/stop CodeWeaver") -- Common pattern in server software (postgres, redis, nginx) -- Clear separation from MCP server concerns - -### 2. MCP Server Command (Unchanged Name, Enhanced Behavior) - -**Command:** `cw server [options]` - -**New Behavior:** -- Automatically starts background services if not running -- `--no-auto-start` flag to disable auto-start (advanced use) -- Clear console output indicating service status - -**Rationale:** -- Maintains backward compatibility -- Provides smart defaults (most users don't need manual service management) -- Explicit control available when needed - -### 3. Enhanced Status Command - -**Command:** `cw status [options]` - -**New Flags:** -- `--services` - Show only background services status -- `--index` - Show only index status (current behavior) -- (no flags) - Comprehensive status (all systems) - -**Rationale:** -- Single command for all observability needs -- Progressive disclosure (detailed when needed) -- Consistent with existing `--watch` flag - -## What Was Removed from Original Plan - -### ❌ Removed: `cw start` / `cw serve` shortcuts - -**Original proposal:** -- `cw start` β†’ shortcut for stdio server -- `cw serve` β†’ shortcut for HTTP server - -**Why removed:** -- Confusing distinction between "start" and "serve" -- Namespace pollution at top level -- stdio clients launch server automatically (no manual start needed) -- HTTP users can type `cw server` (minimal friction) - -### ❌ Removed: `cw server start` / `cw server stop` - -**Original proposal:** -- Make `server` a command group with `start` and `stop` subcommands - -**Why removed:** -- Breaking change (current `cw server` behavior) -- Inconsistent with other direct commands (`index`, `search`) -- `stop` implementation incomplete (TODO comment) -- Server stop better handled by process managers (systemd, docker) - -### ❌ Removed: Incomplete `server stop` feature - -**Original proposal:** -- `cw server stop` via pidfile management - -**Why removed:** -- Marked as TODO in plan (incomplete) -- Cross-platform pidfile complexity -- Production uses systemd/docker/etc (not direct CLI) -- False promise to users (command exists but doesn't work) - -## Final CLI Structure - -```bash -# Background services -cw start # Start services -cw stop # Stop services - -# MCP server -cw server # Start MCP server (auto-starts services) -cw server --no-auto-start # Start MCP only (advanced) -cw server --transport stdio # Specify transport -cw server --transport streamable-http - -# Manual operations -cw index # Manual indexing -cw search # CLI search - -# Observability -cw status # Comprehensive status -cw status --services # Services-only -cw status --index # Index-only -cw status --watch # Continuous monitoring - -# Existing commands (unchanged) -cw config -cw doctor -cw init -cw list / ls -``` - -## User Workflows - -### Quick Start (Most Common) - -```bash -# User just wants to start CodeWeaver -cw server - -# Output: -# [dim]Background services not running, starting...[/dim] -# [green]βœ“[/green] Background services started -# [green]Starting MCP server on 127.0.0.1:9328[/green] -# [dim]Management server: http://127.0.0.1:9329[/dim] -``` - -### Explicit Service Management (Advanced) - -```bash -# Start services separately -cw start - -# Later, start MCP server -cw server --no-auto-start -``` - -### Status Monitoring - -```bash -# Check everything -cw status - -# Check just background services -cw status --services - -# Continuous monitoring -cw status --watch -``` - -### Shutdown - -```bash -# Stop background services (also stops dependent MCP servers) -cw stop -``` - -## Help Text Example - -``` -$ cw --help - -CodeWeaver: Powerful code search for humans and agents - -Commands: - start Start background services (indexing, file watching, telemetry) - stop Stop background services - server Start MCP server (auto-starts services if needed) - index Manual indexing operations - search Search codebase from command line - status Show system status (services, indexing, health) - config Manage configuration - doctor Validate setup - init Initialize configuration - list, ls List available resources -``` - -## Benefits - -### 1. Simplicity -- βœ… Minimal commands for common workflows -- βœ… Smart defaults reduce cognitive load -- βœ… Clear, intuitive naming - -### 2. Backward Compatibility -- βœ… `cw server` still works (no breaking changes) -- βœ… Existing documentation remains valid -- βœ… Deployment scripts unchanged - -### 3. Discoverability -- βœ… Help text clearly explains each command -- βœ… No confusing overlapping commands -- βœ… Progressive disclosure (simple β†’ advanced) - -### 4. Architecture Alignment -- βœ… Reflects actual system architecture (services + MCP layer) -- βœ… Clear separation of concerns -- βœ… Room for future expansion - -## Implementation Notes - -### Auto-Start Mechanism - -```python -@cyclopts.command -def server(..., no_auto_start: bool = False): - """Start CodeWeaver MCP server.""" - - if not no_auto_start: - if not asyncio.run(is_services_running()): - console.print("[dim]Background services not running, starting...[/dim]") - # Start services in background thread - start_services_in_background() - console.print("[green]βœ“[/green] Background services started") -``` - -### Service Detection - -Services running status detected via management server health endpoint: -- Management server runs on port 9329 (always HTTP) -- Health check: `GET http://127.0.0.1:9329/health` -- Returns 200 OK if services running - -### Graceful Shutdown - -Services shutdown via management server API: -- Shutdown endpoint: `POST http://127.0.0.1:9329/shutdown` -- Triggers graceful shutdown of all background services -- MCP servers depending on services will also stop - -## Migration from Original Plan - -### Code Changes - -**Remove:** -- `server_app = cyclopts.App("server")` (command group) -- `@server_app.command def start()` (subcommand) -- `@server_app.command def stop()` (incomplete feature) -- Top-level `start` and `serve` convenience commands - -**Add:** -- `cli/commands/services.py` with `start()` and `stop()` commands -- `--no-auto-start` flag to `server()` command -- Auto-start logic in `server()` command -- Enhanced `status()` command with filtering flags - -### Documentation Changes - -**Update:** -- README Quick Start section -- CLI Commands reference -- MCP client configuration examples (stdio args) -- Help text and docstrings - -**Add:** -- Background services management section -- Auto-start behavior explanation -- Relationship between `index` and background indexing - -## Future Considerations - -### If Moving to Separate-Process Architecture - -Current design supports evolution: -- `cw start` already manages background services independently -- `cw server` already treats services as external dependency -- Clean migration path: services become actual separate process - -### Additional Commands (Future) - -Possible future additions without breaking current structure: -- `cw restart` - Restart background services -- `cw reload` - Reload configuration without full restart -- `cw logs` - Stream service logs -- `cw health` - Detailed health diagnostics - -## Conclusion - -The revised CLI structure: -- **Maintains simplicity** for common workflows -- **Preserves backward compatibility** with existing usage -- **Reflects architecture** accurately (services + MCP) -- **Provides clear boundaries** between different concerns -- **Enables future expansion** without breaking changes - -Users get: -- One command to start CodeWeaver: `cw server` -- Clear control when needed: `cw start`, `cw stop` -- Comprehensive observability: `cw status` -- No confusing overlaps or incomplete features diff --git a/claudedocs/client-pooling-analysis.md b/claudedocs/client-pooling-analysis.md deleted file mode 100644 index fe001db8..00000000 --- a/claudedocs/client-pooling-analysis.md +++ /dev/null @@ -1,631 +0,0 @@ - - -# Client Pooling Analysis & Implementation Strategy - -## Executive Summary - -CodeWeaver currently creates individual HTTP clients for each provider (Voyage AI, Cohere, Qdrant) without connection pooling. This leads to: -- **Connection overhead** from repeated TCP handshakes -- **Resource waste** from creating/destroying connections -- **Potential connection exhaustion** during high-load indexing -- **Qdrant timeout issues** (httpcore.ReadError) due to connection instability - -**Recommendation**: Implement shared HTTP client pools at the application state level with configurable limits and proper lifecycle management. - ---- - -## Current State Analysis - -### Providers Using httpx - -CodeWeaver already tracks which providers use httpx via `Provider.other_env_vars` (see `src/codeweaver/providers/provider.py:118-125`). - -**Providers with httpx support** (have `httpx_env_vars` in their config): - -| Provider | Use Case | httpx Support | -|----------|----------|---------------| -| **VOYAGE** | Embedding, Reranking | βœ… Direct | -| **COHERE** | Embedding, Reranking | βœ… Direct | -| **MISTRAL** | Embedding | βœ… Direct | -| **TAVILY** | Web Search | βœ… Direct | -| **GOOGLE** | Agent | βœ… Direct | -| **HUGGINGFACE_INFERENCE** | Various | βœ… Direct | -| **OpenAI-compatible** | Agent, Embedding | βœ… Direct | -| - OPENAI, FIREWORKS, GITHUB, X_AI | | | -| - GROQ, MOONSHOT, OLLAMA | | | -| - OPENROUTER, PERPLEXITY, CEREBRAS | | | -| **AZURE** | Multiple (OpenAI, Cohere on Azure) | βœ… Direct | -| **VERCEL** | Agent | βœ… Direct | -| **TOGETHER** | Agent | βœ… Direct | -| **HEROKU** | Agent | βœ… Direct | -| **DEEPSEEK** | Agent | βœ… Direct | - -**Providers NOT using httpx**: -- **QDRANT**: Uses httpx internally via qdrant_client but doesn't expose `httpx_env_vars` -- **BEDROCK**: Uses boto3 (AWS SDK), not httpx -- **FASTEMBED, MEMORY, SENTENCE_TRANSFORMERS**: Local providers - -### Client Instantiation Patterns - -#### 1. **Voyage AI** (src/codeweaver/providers/embedding/providers/voyage.py) -```python -# Created per-provider instance, NO connection pooling -from voyageai.client_async import AsyncClient - -client = AsyncClient(api_key=api_key) # Line 120 -``` - -#### 2. **Cohere** (src/codeweaver/providers/embedding/providers/cohere.py) -```python -# Created per-provider instance -from cohere import AsyncClientV2 - -client = CohereClient(api_key=api_key, **client_options) -``` - -#### 3. **Qdrant** (implicitly uses httpx via qdrant_client) -```python -# qdrant_client internally uses httpx but we don't control pooling -await self._client.upsert(collection_name=collection_name, points=points) -``` - -### Problems Identified - -1. **No Connection Reuse**: Each provider creates fresh connections per request -2. **No Pool Limits**: Unbounded connection creation during batch operations -3. **No Shared Infrastructure**: Each provider manages its own HTTP client -4. **Connection Lifecycle**: Clients created at provider init, but no explicit cleanup -5. **Qdrant Instability**: Connection errors (httpcore.ReadError) suggest pool exhaustion or timeout issues - ---- - -## Connection Pooling Architecture - -### httpx Connection Pooling - -Both `voyageai` and `cohere` use `httpx` internally, which DOES support connection pooling: - -```python -import httpx - -# httpx.AsyncClient has built-in connection pooling -limits = httpx.Limits( - max_connections=100, # Total connections across all hosts - max_keepalive_connections=20, # Persistent connections to keep alive - keepalive_expiry=5.0, # Seconds to keep connections alive -) - -timeout = httpx.Timeout( - connect=10.0, # Connection timeout - read=30.0, # Read timeout - write=10.0, # Write timeout - pool=5.0 # Pool acquire timeout -) - -client = httpx.AsyncClient(limits=limits, timeout=timeout) -``` - -### Current Library Support - -**Voyage AI**: -- `AsyncClient` accepts `max_retries` and timeout configs -- Internally uses `httpx` but doesn't expose pool configuration -- **Action**: Pass custom `httpx_client` to Voyage's AsyncClient - -**Cohere**: -- `AsyncClientV2` accepts `httpx_client` parameter -- Full control over connection pooling -- **Action**: Create shared httpx client and pass to Cohere - -**Qdrant**: -- Uses `httpx` internally via `qdrant_client` -- Provides `timeout` parameter but no direct pool control -- **Action**: May need to patch or configure at qdrant_client level - ---- - -## Proposed Implementation - -### 0. Helper Method for Provider Detection - -Add a helper method to `Provider` enum to identify httpx-using providers: - -**File**: `src/codeweaver/providers/provider.py` - -```python -@property -def uses_httpx(self) -> bool: - """Check if the provider uses httpx for HTTP connections. - - Providers that use httpx can benefit from connection pooling. - Determined by checking if httpx_env_vars are present in other_env_vars. - """ - if env_vars := self.other_env_vars: - if isinstance(env_vars, tuple): - # Check all env var dicts for httpx proxy or ssl settings - return any( - "http_proxy" in env_dict or "ssl_cert_file" in env_dict or - (env_dict.get("other") and ("http_proxy" in env_dict["other"] or "ssl_cert_file" in env_dict["other"])) - for env_dict in env_vars - ) - return False -``` - -### 1. Centralized HTTP Client Pool Manager - -Create a singleton HTTP client manager at application state level: - -**File**: `src/codeweaver/common/http_pool.py` - -```python -from __future__ import annotations - -import httpx -import logging -from typing import ClassVar -from dataclasses import dataclass - -logger = logging.getLogger(__name__) - -@dataclass(frozen=True) -class PoolLimits: - """HTTP connection pool limits configuration.""" - max_connections: int = 100 - max_keepalive_connections: int = 20 - keepalive_expiry: float = 5.0 - -@dataclass(frozen=True) -class PoolTimeouts: - """HTTP timeout configuration.""" - connect: float = 10.0 - read: float = 60.0 # Longer for embedding/vector operations - write: float = 10.0 - pool: float = 5.0 - -class HttpClientPool: - """Singleton HTTP client pool manager for provider connections.""" - - _instance: ClassVar[HttpClientPool | None] = None - _clients: dict[str, httpx.AsyncClient] - _limits: PoolLimits - _timeouts: PoolTimeouts - - def __init__( - self, - limits: PoolLimits | None = None, - timeouts: PoolTimeouts | None = None - ): - self._clients = {} - self._limits = limits or PoolLimits() - self._timeouts = timeouts or PoolTimeouts() - - @classmethod - def get_instance(cls) -> HttpClientPool: - """Get singleton instance.""" - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def get_client(self, name: str, **overrides) -> httpx.AsyncClient: - """Get or create a pooled HTTP client for a specific provider. - - Args: - name: Provider name (e.g., 'voyage', 'cohere', 'qdrant') - **overrides: Override default limits/timeouts for this client - - Returns: - Configured httpx.AsyncClient with connection pooling - """ - if name not in self._clients: - limits = httpx.Limits( - max_connections=overrides.get('max_connections', self._limits.max_connections), - max_keepalive_connections=overrides.get( - 'max_keepalive_connections', - self._limits.max_keepalive_connections - ), - keepalive_expiry=overrides.get('keepalive_expiry', self._limits.keepalive_expiry), - ) - - timeout = httpx.Timeout( - connect=overrides.get('connect_timeout', self._timeouts.connect), - read=overrides.get('read_timeout', self._timeouts.read), - write=overrides.get('write_timeout', self._timeouts.write), - pool=overrides.get('pool_timeout', self._timeouts.pool), - ) - - self._clients[name] = httpx.AsyncClient( - limits=limits, - timeout=timeout, - http2=overrides.get('http2', True), # Enable HTTP/2 for better multiplexing - ) - - logger.info( - "Created HTTP client pool for %s: max_conn=%d, keepalive=%d", - name, limits.max_connections, limits.max_keepalive_connections - ) - - return self._clients[name] - - async def close_all(self) -> None: - """Close all pooled clients (cleanup on shutdown).""" - for name, client in self._clients.items(): - try: - await client.aclose() - logger.info("Closed HTTP client pool for %s", name) - except Exception: - logger.exception("Error closing HTTP client pool for %s", name) - self._clients.clear() - - async def __aenter__(self) -> HttpClientPool: - return self - - async def __aexit__(self, *args) -> None: - await self.close_all() -``` - -### 2. Integration into CodeWeaverState - -**File**: `src/codeweaver/server/server.py` - -```python -from codeweaver.common.http_pool import HttpClientPool - -@dataclass(...) -class CodeWeaverState(DataclassSerializationMixin): - # ... existing fields ... - - http_pool: Annotated[ - HttpClientPool, - Field( - default_factory=HttpClientPool.get_instance, - description="Shared HTTP client pool for provider connections", - exclude=True, - ), - ] = None -``` - -**Cleanup in lifespan**: - -```python -async def _cleanup_state( - state: CodeWeaverState, - indexing_task: asyncio.Task | None, - status_display: Any, - *, - verbose: bool = False, -) -> None: - """Clean up application state and shutdown services.""" - # ... existing cleanup ... - - # Close HTTP client pools - if state.http_pool: - try: - await state.http_pool.close_all() - if verbose: - _logger.info("Closed HTTP client pools") - except Exception: - _logger.exception("Error closing HTTP client pools") -``` - -### 3. Provider Integration - -#### Voyage AI Provider - -**File**: `src/codeweaver/providers/embedding/providers/voyage.py` - -```python -def __init__( - self, - client: AsyncClient | None = None, - caps: EmbeddingModelCapabilities | None = None, - **kwargs: Any, -) -> None: - # ... validation logic ... - - if client is None: - from codeweaver.common.http_pool import HttpClientPool - from codeweaver.server.server import get_state - - # Get shared HTTP client from pool - try: - state = get_state() - httpx_client = state.http_pool.get_client( - 'voyage', - max_connections=50, # Voyage-specific limits - read_timeout=90.0, # Longer for embedding operations - ) - except Exception: - # Fallback if state not initialized (e.g., during testing) - httpx_client = None - - if api_key := kwargs.pop("api_key", None) or os.getenv("VOYAGE_API_KEY"): - if isinstance(api_key, SecretStr): - api_key = api_key.get_secret_value() - client = AsyncClient( - api_key=api_key, - httpx_client=httpx_client # Use pooled client - ) - else: - client = AsyncClient(httpx_client=httpx_client) - - # ... rest of initialization ... -``` - -#### Cohere Provider - -**File**: `src/codeweaver/providers/embedding/providers/cohere.py` - -```python -def __init__( - self, - client: CohereClient | None = None, - caps: EmbeddingModelCapabilities | None = None, - **kwargs: Any, -) -> None: - # ... validation ... - - if not client: - from codeweaver.common.http_pool import HttpClientPool - from codeweaver.server.server import get_state - - try: - state = get_state() - httpx_client = state.http_pool.get_client( - 'cohere', - max_connections=50, - read_timeout=90.0, - ) - except Exception: - httpx_client = None - - client_options = kwargs.get("client_options", {}) - client_options['httpx_client'] = httpx_client - - api_key = # ... existing API key logic ... - client = CohereClient(api_key=api_key, **client_options) - - # ... rest of initialization ... -``` - -#### Qdrant Provider - -**File**: `src/codeweaver/providers/vector_stores/qdrant_base.py` - -```python -def __post_init__(self) -> None: - """Initialize the Qdrant client with connection pooling.""" - from codeweaver.common.http_pool import HttpClientPool - from codeweaver.server.server import get_state - - # Get pooled HTTP client for Qdrant - try: - state = get_state() - httpx_client = state.http_pool.get_client( - 'qdrant', - max_connections=30, # Conservative for vector store - read_timeout=120.0, # Very long for large upserts - keepalive_expiry=30.0, # Longer keepalive for Qdrant - ) - except Exception: - httpx_client = None - - # qdrant_client doesn't expose httpx_client parameter directly - # We need to pass timeout configs instead - timeout_config = { - 'timeout': 120.0, # Overall timeout - } - - self._client = AsyncQdrantClient( - **self._get_client_kwargs(), - **timeout_config, - ) -``` - ---- - -## Configuration - -Add HTTP pool settings to configuration: - -**File**: `src/codeweaver/config/settings.py` - -```python -@dataclass -class HttpPoolSettings: - """HTTP client pool configuration.""" - max_connections: int = 100 - max_keepalive_connections: int = 20 - keepalive_expiry: float = 5.0 - connect_timeout: float = 10.0 - read_timeout: float = 60.0 - write_timeout: float = 10.0 - pool_timeout: float = 5.0 - enable_http2: bool = True - -class CodeWeaverSettings(BasedModel): - # ... existing fields ... - http_pool: HttpPoolSettings = Field(default_factory=HttpPoolSettings) -``` - ---- - -## Benefits - -### 1. **Performance Improvements** -- βœ… **Reduced Latency**: Reuse existing connections (save ~100-300ms per request) -- βœ… **Better Throughput**: HTTP/2 multiplexing allows multiple requests over single connection -- βœ… **Lower CPU**: Avoid repeated TLS handshakes - -### 2. **Reliability Improvements** -- βœ… **Fixes Qdrant Errors**: Connection pool prevents httpcore.ReadError from exhaustion -- βœ… **Predictable Resource Usage**: Bounded connection limits prevent resource exhaustion -- βœ… **Graceful Degradation**: Pool timeouts prevent cascading failures - -### 3. **Operational Benefits** -- βœ… **Centralized Control**: Single point for HTTP configuration -- βœ… **Observable**: Can add metrics on pool usage -- βœ… **Configurable**: Per-provider tuning based on API characteristics - ---- - -## Migration Strategy - -### Phase 1: Infrastructure (Week 1) -1. Create `HttpClientPool` class -2. Add to `CodeWeaverState` -3. Add cleanup to lifespan -4. Add configuration settings - -### Phase 2: Provider Integration (Week 2) -1. Update Voyage AI provider -2. Update Cohere provider -3. Update Qdrant provider (if possible) -4. Fallback handling for standalone usage - -### Phase 3: Testing & Tuning (Week 3) -1. Load testing with large repos -2. Monitor connection pool metrics -3. Tune limits per provider -4. Document performance improvements - -### Phase 4: Advanced Features (Future) -1. Add pool metrics endpoint -2. Dynamic pool sizing based on load -3. Per-operation timeout overrides -4. Connection health monitoring - ---- - -## Testing Plan - -### Unit Tests -```python -async def test_http_pool_singleton(): - pool1 = HttpClientPool.get_instance() - pool2 = HttpClientPool.get_instance() - assert pool1 is pool2 - -async def test_client_reuse(): - pool = HttpClientPool() - client1 = pool.get_client('test') - client2 = pool.get_client('test') - assert client1 is client2 - -async def test_cleanup(): - pool = HttpClientPool() - client = pool.get_client('test') - await pool.close_all() - # Verify client is closed -``` - -### Integration Tests -```python -async def test_voyage_with_pooling(): - # Index large repo and verify connection reuse - # Monitor metrics for connection count - pass - -async def test_qdrant_stability(): - # Run prolonged indexing - # Verify no httpcore.ReadError occurs - pass -``` - -### Load Tests -- Index 10K+ files concurrently -- Monitor connection pool metrics -- Verify no connection exhaustion -- Measure latency improvements - ---- - -## Monitoring & Metrics - -### Pool Metrics to Track -```python -class PoolMetrics: - active_connections: int - idle_connections: int - total_requests: int - connection_timeouts: int - pool_exhaustions: int -``` - -### Health Endpoint Integration -Add to `/health` endpoint: -```json -{ - "http_pools": { - "voyage": { - "active": 5, - "idle": 15, - "total_requests": 1523, - "timeouts": 0 - }, - "cohere": {...}, - "qdrant": {...} - } -} -``` - ---- - -## Risks & Mitigations - -### Risk 1: Breaking Changes -**Impact**: Providers might not work with pooled clients -**Mitigation**: -- Fallback to non-pooled clients if state unavailable -- Extensive testing before rollout -- Feature flag for gradual rollout - -### Risk 2: Connection Leaks -**Impact**: Connections not properly closed -**Mitigation**: -- Explicit cleanup in lifespan -- Context manager pattern -- Connection leak detection in tests - -### Risk 3: Provider API Changes -**Impact**: New versions might change client APIs -**Mitigation**: -- Pin library versions -- Version compatibility testing -- Graceful degradation - ---- - -## Alternative Approaches Considered - -### 1. **Per-Provider Pools** (Rejected) -Each provider manages its own pool - lacks centralization - -### 2. **Global httpx Client** (Rejected) -Single client for all providers - loses per-provider tuning - -### 3. **Connection Pool Proxy** (Overkill) -Dedicated connection pooling service - too complex for current needs - ---- - -## References - -- **httpx Pooling**: https://www.python-httpx.org/advanced/#pool-limit-configuration -- **Voyage AI Client**: https://github.com/voyage-ai/voyage-python-client -- **Cohere SDK**: https://github.com/cohere-ai/cohere-python -- **Qdrant Client**: https://github.com/qdrant/qdrant-client - ---- - -## Next Steps - -1. Review this document with team -2. Create implementation issues -3. Set up development branch -4. Begin Phase 1 implementation -5. Set up monitoring infrastructure diff --git a/claudedocs/connection-pooling-implementation-plan.md b/claudedocs/connection-pooling-implementation-plan.md deleted file mode 100644 index 6516647f..00000000 --- a/claudedocs/connection-pooling-implementation-plan.md +++ /dev/null @@ -1,285 +0,0 @@ - - -# Connection Pooling Implementation Plan - -**Status**: βœ… Implemented -**Priority**: High -**Branch**: `claude/plan-connection-pooling-011nqd3xSBSn6a1FQdjnf8Qg` -**Based on**: `claudedocs/client-pooling-analysis.md` - ---- - -## Overview - -This document outlines the immediate implementation plan for HTTP connection pooling across CodeWeaver's HTTP-based providers. The implementation will be phased, starting with core infrastructure and progressing to provider integration. - -## Phase 1: Infrastructure (Core Implementation) - -### Task 1.1: Create HttpClientPool Class - -**File**: `src/codeweaver/common/http_pool.py` - -Create a centralized HTTP client pool manager with: -- Singleton pattern for application-wide access -- Configurable connection limits and timeouts -- Per-provider client management -- HTTP/2 support for better multiplexing -- Proper async cleanup - -**Key Components**: -```python -@dataclass(frozen=True) -class PoolLimits: - max_connections: int = 100 - max_keepalive_connections: int = 20 - keepalive_expiry: float = 5.0 - -@dataclass(frozen=True) -class PoolTimeouts: - connect: float = 10.0 - read: float = 60.0 - write: float = 10.0 - pool: float = 5.0 - -class HttpClientPool: - # Singleton instance - # Per-provider client dict - # get_client(name, **overrides) method - # close_all() cleanup method -``` - -### Task 1.2: Add Configuration Settings - -**File**: `src/codeweaver/config/settings.py` - -Add `HttpPoolSettings` TypedDict for configuration: -```python -class HttpPoolSettings(TypedDict, total=False): - max_connections: int - max_keepalive_connections: int - keepalive_expiry: float - connect_timeout: float - read_timeout: float - write_timeout: float - pool_timeout: float - enable_http2: bool -``` - -### Task 1.3: Integrate into CodeWeaverState - -**File**: `src/codeweaver/server/server.py` - -Add `http_pool` field to `CodeWeaverState`: -```python -http_pool: Annotated[ - HttpClientPool | None, - Field( - default=None, - description="Shared HTTP client pool for provider connections", - exclude=True, - ), -] = None -``` - -Initialize in `_initialize_cw_state()` function. - -### Task 1.4: Add Cleanup to Lifespan - -**File**: `src/codeweaver/server/server.py` - -Update `_cleanup_state()` to close HTTP pools: -```python -# Close HTTP client pools -if state.http_pool: - try: - await state.http_pool.close_all() - except Exception: - _logger.exception("Error closing HTTP client pools") -``` - ---- - -## Phase 2: Provider Integration - -### Task 2.1: Update Voyage AI Provider - -**File**: `src/codeweaver/providers/embedding/providers/voyage.py` - -The Voyage AI `AsyncClient` accepts an `httpx_client` parameter. We'll modify initialization to use the pooled client. - -**Current Flow**: -```python -client = AsyncClient(api_key=api_key) -``` - -**New Flow**: -```python -def _get_pooled_httpx_client() -> httpx.AsyncClient | None: - """Get pooled HTTP client for Voyage AI.""" - try: - from codeweaver.server.server import get_state - state = get_state() - if state.http_pool: - return state.http_pool.get_client( - 'voyage', - max_connections=50, - read_timeout=90.0, - ) - except Exception: - pass # Fallback to default client - return None - -# In provider initialization -httpx_client = _get_pooled_httpx_client() -client = AsyncClient(api_key=api_key, httpx_client=httpx_client) -``` - -### Task 2.2: Update Cohere Provider - -**File**: `src/codeweaver/providers/embedding/providers/cohere.py` - -The Cohere `AsyncClientV2` accepts an `httpx_client` parameter in `client_options`. - -**Current Flow** (line 123): -```python -known_client_options = { - "api_key", "base_url", "timeout", "max_retries", "httpx_client", -} -``` - -**New Flow**: Add pooled client to options: -```python -def _get_pooled_httpx_client() -> httpx.AsyncClient | None: - """Get pooled HTTP client for Cohere.""" - try: - from codeweaver.server.server import get_state - state = get_state() - if state.http_pool: - return state.http_pool.get_client( - 'cohere', - max_connections=50, - read_timeout=90.0, - ) - except Exception: - pass - return None - -# In __init__ before creating client: -if not client_options.get("httpx_client"): - client_options["httpx_client"] = _get_pooled_httpx_client() -``` - -### Task 2.3: Qdrant Considerations - -**File**: `src/codeweaver/providers/vector_stores/qdrant_base.py` - -The `qdrant_client` library uses httpx internally but doesn't expose an `httpx_client` parameter. We have two options: - -1. **Use timeout configuration** (recommended for now): - - Configure longer timeouts via `qdrant_client` parameters - - This addresses the httpcore.ReadError issues - -2. **Future enhancement**: - - Investigate if newer qdrant_client versions expose httpx configuration - - Consider contributing upstream if needed - ---- - -## Implementation Order - -``` -1. Create http_pool.py module -2. Add HttpPoolSettings to config -3. Add http_pool to CodeWeaverState -4. Add cleanup to _cleanup_state() -5. Update Voyage AI provider -6. Update Cohere provider -7. Add unit tests -8. Integration testing -``` - ---- - -## Provider-Specific Settings - -| Provider | max_connections | read_timeout | keepalive_expiry | HTTP/2 | Parameter Name | -|----------|----------------|--------------|------------------|--------|----------------| -| Voyage | 50 | 90s | 5s | Yes | `httpx_client` | -| Cohere | 50 | 90s | 5s | Yes | `httpx_client` | -| OpenAI | 50 | 90s | 5s | Yes | `http_client` | -| Azure | 50 | 90s | 5s | Yes | `http_client` | -| Fireworks| 50 | 90s | 5s | Yes | `http_client` | -| Groq | 50 | 90s | 5s | Yes | `http_client` | -| Together | 50 | 90s | 5s | Yes | `http_client` | -| Ollama | 50 | 90s | 5s | Yes | `http_client` | -| Cerebras | 50 | 90s | 5s | Yes | `http_client` | -| Heroku | 50 | 90s | 5s | Yes | `http_client` | -| Mistral | 50 | 90s | 5s | Yes | `httpx_client` | -| Qdrant | N/A | N/A | N/A | N/A | Not supported | - -**Note**: Qdrant uses httpx internally but doesn't expose a client injection parameter. - ---- - -## Testing Strategy - -### Unit Tests -- `test_http_pool_singleton()` - Verify singleton behavior -- `test_client_reuse()` - Verify client caching -- `test_cleanup()` - Verify proper cleanup -- `test_provider_overrides()` - Verify per-provider settings - -### Integration Tests -- Test Voyage AI with pooled client -- Test Cohere with pooled client -- Verify connection reuse (check logs) - -### Manual Verification -- Run `cw index` on a large repo -- Monitor for httpcore.ReadError (should be eliminated) -- Check connection reuse in debug logs - ---- - -## Rollback Plan - -The implementation includes fallbacks: -1. If `get_state()` fails, providers fall back to default clients -2. If `http_pool` is None, providers create their own clients -3. No breaking changes to existing interfaces - ---- - -## Success Metrics - -1. **No httpcore.ReadError** during indexing operations -2. **Reduced connection overhead** (visible in debug logs) -3. **All existing tests pass** without modification -4. **Memory usage stable** during long indexing operations - ---- - -## Files to Create/Modify - -| File | Action | Description | -|------|--------|-------------| -| `src/codeweaver/common/http_pool.py` | **Create** | HTTP client pool manager | -| `src/codeweaver/config/settings.py` | Modify | Add HttpPoolSettings | -| `src/codeweaver/server/server.py` | Modify | Add http_pool to state, cleanup | -| `src/codeweaver/providers/embedding/providers/voyage.py` | Modify | Use pooled client | -| `src/codeweaver/providers/embedding/providers/cohere.py` | Modify | Use pooled client | -| `tests/unit/common/test_http_pool.py` | **Create** | Unit tests | - ---- - -## Notes - -- The implementation prioritizes backward compatibility -- Fallbacks ensure the system works without pooling if needed -- HTTP/2 is enabled by default for better multiplexing on modern APIs -- Qdrant integration may require upstream changes to qdrant_client diff --git a/claudedocs/option-a-service-implementation-plan-final.md b/claudedocs/option-a-service-implementation-plan-final.md deleted file mode 100644 index 1bdef4ce..00000000 --- a/claudedocs/option-a-service-implementation-plan-final.md +++ /dev/null @@ -1,1699 +0,0 @@ - - -# Option A: Service Architecture - Implementation Plan (FINAL) - -> **Revision History**: -> - **v1**: Initial plan with server/background services separation -> - **v2**: Updated for CLI UX improvements and management server separation -> - **v3 (FINAL)**: Corrected middleware architecture, simplified state management, refined based on architecture review - -> **Latest Update**: Document revised based on comprehensive architecture review: -> - **Middleware Correction**: Use FastMCP middleware (not Starlette) for telemetry and state access -> - **State Management**: Repurpose existing AppState as BackgroundState (minimal disruption) -> - **Configuration**: Background service settings in ServerSettings (not IndexerSettings) -> - **Management Server**: Direct route registration (reuse existing handlers) -> - **Information Flow**: Clarified that existing patterns already handle all required data flows - -## Executive Summary - -**Goal**: Separate CodeWeaver's background processes (indexing, file watching) from the MCP protocol layer, enabling full stdio support and proper process lifecycle management. - -**Architectural Approach**: Same-process separation with clear boundaries, designed to evolve to separate-process architecture if needed. - -**Timeline**: 3-4 weeks for alpha.2 release -**Breaking Changes**: Minimal - primarily internal architecture refactoring -**Benefits**: -- Full stdio support (not read-only) -- Clean separation of concerns (protocol vs. business logic) -- Foundation for future separate-process deployment -- Proper lifecycle management for background tasks -- Maintains CodeWeaver's ONE TOOL principle - ---- - -## Architecture Overview - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ CodeWeaver MCP Server Process (FastMCP + Starlette) β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ MCP Protocol Layer (FastMCP) β”‚ β”‚ -β”‚ β”‚ - Handles MCP protocol (HTTP or stdio) β”‚ β”‚ -β”‚ β”‚ - Port 9328 (HTTP mode only) β”‚ β”‚ -β”‚ β”‚ - Exposes ONE TOOL: find_code() β”‚ β”‚ -β”‚ β”‚ - FastMCP Middleware Stack: β”‚ β”‚ -β”‚ β”‚ * StatisticsMiddleware (telemetry) β”‚ β”‚ -β”‚ β”‚ * ErrorHandlingMiddleware β”‚ β”‚ -β”‚ β”‚ * RateLimitingMiddleware β”‚ β”‚ -β”‚ β”‚ - No observability endpoints here β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ ↓ Access via global getter: get_background_state() -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Management Server (Always HTTP - Port 9329) β”‚ β”‚ -β”‚ β”‚ - Independent of MCP transport choice β”‚ β”‚ -β”‚ β”‚ - /health, /status, /metrics, /version β”‚ β”‚ -β”‚ β”‚ - /settings, /state β”‚ β”‚ -β”‚ β”‚ - Available for stdio and HTTP MCP modes β”‚ β”‚ -β”‚ β”‚ - Runs via separate uvicorn instance β”‚ β”‚ -β”‚ β”‚ - Started automatically with background services β”‚ β”‚ -β”‚ β”‚ - Reuses handlers from app_bindings.py β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ ↓ Both access BackgroundState via app.state β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Background Services Layer (BackgroundState) β”‚ β”‚ -β”‚ β”‚ - Renamed from AppState (minimal disruption) β”‚ β”‚ -β”‚ β”‚ - ProviderRegistry (singleton) β”‚ β”‚ -β”‚ β”‚ - IndexerService (file watching + indexing) β”‚ β”‚ -β”‚ β”‚ - HealthService (monitoring) β”‚ β”‚ -β”‚ β”‚ - SessionStatistics (telemetry) β”‚ β”‚ -β”‚ β”‚ - VectorStoreFailoverManager (resilience) β”‚ β”‚ -β”‚ β”‚ - ManagementServer (reference) β”‚ β”‚ -β”‚ β”‚ - background_tasks: set[asyncio.Task] β”‚ β”‚ -β”‚ β”‚ - shutdown_event: asyncio.Event β”‚ β”‚ -β”‚ β”‚ - Managed via Starlette lifespan β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ ↓ Providers β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Provider Layer (ProviderRegistry) β”‚ β”‚ -β”‚ β”‚ - Indexer (core indexing engine) β”‚ β”‚ -β”‚ β”‚ - VectorStore (Qdrant) β”‚ β”‚ -β”‚ β”‚ - Embedder (Voyage AI) β”‚ β”‚ -β”‚ β”‚ - Sparse Embedder (local SPLADE) β”‚ β”‚ -β”‚ β”‚ - Reranker β”‚ β”‚ -β”‚ β”‚ - FailoverManager (resilience) β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - ↑ MCP Protocol (HTTP:9328 or stdio) -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ MCP Clients β”‚ Monitoring: http://127.0.0.1:9329 -β”‚ - Claude Desktop β”‚ (health, status, metrics) -β”‚ - Cursor β”‚ -β”‚ - Continue β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**Key Principles**: -1. **Constitutional Compliance**: ONE TOOL (`find_code`) exposed to agents -2. **Pattern Reuse**: Leverage existing AppState β†’ rename to BackgroundState -3. **Same-Process First**: Clear boundaries, evolve to separate-process if needed -4. **Cross-Platform**: HTTP-first (Unix sockets deferred to later phase) -5. **Immutability**: Frozen models, get_settings_map() for read-only access -6. **Transport Independence**: Management endpoints always available (stdio or HTTP) -7. **FastMCP Middleware**: Use FastMCP middleware for MCP protocol concerns, not Starlette - ---- - -## Information Flow Architecture - -### Data Flow 1: Telemetry (find_code β†’ telemetry service) - -**Status**: βœ… Already implemented correctly - -**Implementation**: FastMCP `StatisticsMiddleware` (existing) - -```python -# src/codeweaver/middleware/statistics.py (EXISTING - NO CHANGES NEEDED) - -class StatisticsMiddleware(Middleware): - """FastMCP middleware to track request statistics and performance metrics.""" - - async def on_call_tool( - self, - context: MiddlewareContext[CallToolRequestParams], - call_next: CallNext[CallToolRequestParams, CallToolResult], - ) -> CallToolResult: - """Handle incoming tool requests and track statistics.""" - start_time = time.perf_counter() - request_id = context.fastmcp_context.request_id - - try: - result = await call_next(context) - duration_ms = (time.perf_counter() - start_time) * 1000 - - # Telemetry automatically captured - self.statistics.add_successful_request(request_id=request_id) - self.timing_statistics.update( - "on_call_tool_requests", - duration_ms, - tool_or_resource_name=context.message.name - ) - - return result - except Exception: - # Track failures - self.statistics.add_failed_request(request_id=request_id) - raise -``` - -**Why this works**: -- FastMCP middleware sees ALL MCP operations (stdio and HTTP) -- Automatically captures timing, success/failure, request IDs -- No code changes needed in find_code tool -- Works across all transports - -### Data Flow 2-4: Queries, Results, Status - -**Status**: βœ… Already handled via existing patterns - -**Implementation**: Global getter + ProviderRegistry - -```python -# find_code accesses BackgroundState via global getter -# (No Context injection needed - DI framework planned for future) - -async def find_code( - query: str, - intent: IntentType | None = None, - *, - token_limit: int = 30000, - focus_languages: tuple[str, ...] | None = None, -) -> FindCodeResponseSummary: - # Access background state via global getter - # (same pattern as current get_state()) - from codeweaver.server.background.state import get_background_state - - background_state = get_background_state() - - # Flow 2: Queries to services (via ProviderRegistry) - indexer = background_state.indexer - - # Flow 3: Results from services (via return values) - response = await find_code_impl(query, intent, token_limit, focus_languages, indexer) - - # Flow 4: Status information (via HealthService and failover manager) - if background_state.failover_manager: - failover_metadata = { - "failover": { - "enabled": background_state.failover_manager.backup_enabled, - "active": background_state.failover_manager.is_failover_active, - } - } - response = response.model_copy(update={"metadata": failover_metadata}) - - # SearchEvent telemetry (already implemented in find_code business logic) - # See: src/codeweaver/agent_api/find_code/__init__.py:387-395 - try: - from codeweaver.common.telemetry.events import capture_search_event - - capture_search_event( - response=response, - query=query, - intent_type=intent or IntentType.UNDERSTAND, - strategies=strategies_used, - execution_time_ms=execution_time_ms, - tools_over_privacy=tools_over_privacy, - feature_flags=feature_flags, - ) - except Exception: - # Never fail find_code due to telemetry - logger.debug("Failed to capture search telemetry") - - return response -``` - -**Why this works**: -- Global `get_background_state()` accessible everywhere (same pattern as current `get_state()`) -- ProviderRegistry provides access to all services -- SearchEvent telemetry already implemented in find_code business logic -- No FastMCP middleware needed for SearchEvent (too specific to find_code) -- DI framework planned for future will replace global getter pattern - -**Important Note on Telemetry**: -- **MCP-level timing**: Captured by `StatisticsMiddleware` (FastMCP middleware) -- **SearchEvent telemetry**: Captured in `find_code()` business logic (already implemented at lines 387-395, 408-418) -- SearchEvent has rich context (query, response, intent, strategies, execution time) -- SearchEvent is NOT in middleware because it needs find_code execution context -- No changes needed to telemetry architecture - ---- - -## Phase 1: Background Services Extraction (Week 1-2) - -### 1.1 Rename and Extend AppState β†’ BackgroundState - -**Rationale**: Current AppState is already 90% background services. Rename it rather than creating a new class. - -**File**: `src/codeweaver/server/background/state.py` (NEW - moved from server.py) - -```python -""" -Background services state management. - -Extracted from existing AppState pattern - manages lifecycle of background -services independent of protocol layer. -""" - -from __future__ import annotations - -import asyncio -import time -from pathlib import Path -from typing import TYPE_CHECKING, Annotated - -from pydantic import ConfigDict, DirectoryPath, Field, NonNegativeInt, PrivateAttr, computed_field -from pydantic.dataclasses import dataclass - -from codeweaver.core.types.models import DATACLASS_CONFIG, DataclassSerializationMixin -from codeweaver.common.logging import get_logger - -if TYPE_CHECKING: - from codeweaver.engine.indexer.indexer import Indexer - from codeweaver.common.health import HealthService - from codeweaver.common.statistics import SessionStatistics - from codeweaver.common.registry import ProviderRegistry, ServicesRegistry, ModelRegistry - from codeweaver.providers.failover import VectorStoreFailoverManager - from codeweaver.common.telemetry.client import PostHogClient - from codeweaver.config.settings import CodeWeaverSettings - from codeweaver.core.types import FilteredKeyT, AnonymityConversion - -logger = get_logger(__name__) - - -@dataclass(order=True, kw_only=True, config=DATACLASS_CONFIG | ConfigDict(extra="forbid")) -class BackgroundState(DataclassSerializationMixin): - """ - Background services state (formerly AppState). - - Manages lifecycle of long-running background tasks independent of - MCP protocol concerns. This is the same as the old AppState, just - renamed and extended with background task tracking. - """ - - initialized: Annotated[ - bool, Field(description="Indicates if the background services have been initialized") - ] = False - settings: Annotated[ - CodeWeaverSettings | None, - Field(description="CodeWeaver configuration settings"), - ] - config_path: Annotated[ - Path | None, Field(default=None, description="Path to the configuration file, if any") - ] - project_path: Annotated[ - DirectoryPath, - Field(description="Path to the project root"), - ] - provider_registry: Annotated[ - ProviderRegistry, - Field(description="Provider registry for dynamic provider management"), - ] - services_registry: Annotated[ - ServicesRegistry, - Field(description="Service registry for managing available services"), - ] - model_registry: Annotated[ - ModelRegistry, - Field(description="Model registry for managing AI and embedding/reranking models"), - ] - statistics: Annotated[ - SessionStatistics, - Field(description="Session statistics and performance tracking"), - ] - indexer: Annotated[ - Indexer | None, Field(default=None, description="Indexer instance for background indexing") - ] - health_service: Annotated[ - HealthService | None, Field(description="Health service instance", exclude=True) - ] = None - failover_manager: Annotated[ - VectorStoreFailoverManager | None, - Field(description="Failover manager instance", exclude=True), - ] = None - startup_time: Annotated[ - float, Field(default_factory=time.time, description="Server startup timestamp") - ] - - # NEW: Management server reference - management_server: Annotated[ - ManagementServer | None, - Field(description="Management HTTP server instance", exclude=True) - ] = None - - # NEW: Background task tracking - background_tasks: Annotated[ - set[asyncio.Task], - Field(default_factory=set, description="Set of running background tasks") - ] = field(default_factory=set) - - # NEW: Shutdown coordination - shutdown_event: Annotated[ - asyncio.Event, - Field(default_factory=asyncio.Event, description="Event to signal shutdown") - ] = field(default_factory=asyncio.Event) - - telemetry: Annotated[PostHogClient | None, PrivateAttr()] = None - - def _telemetry_keys(self) -> dict[FilteredKeyT, AnonymityConversion]: - from codeweaver.core.types import AnonymityConversion, FilteredKey - - return { - FilteredKey("config_path"): AnonymityConversion.BOOLEAN, - FilteredKey("project_path"): AnonymityConversion.HASH, - } - - @computed_field - @property - def request_count(self) -> NonNegativeInt: - """Computed field for the number of requests handled by the server.""" - if self.statistics: - return self.statistics.total_requests + (self.statistics.total_http_requests or 0) - return 0 - - async def initialize(self) -> None: - """Initialize background services.""" - if self.initialized: - logger.warning("BackgroundState already initialized") - return - - try: - from codeweaver.config.settings import get_settings_map - from codeweaver.common.registry.provider import ProviderRegistry - - settings_map = get_settings_map() - - # Health service initialization (if not already set) - if not self.health_service: - from codeweaver.server.health_service import HealthService - self.health_service = HealthService( - provider_registry=self.provider_registry, - statistics=self.statistics, - indexer=self.indexer, - startup_time=self.startup_time, - ) - - # Failover manager initialization (if not already set) - if not self.failover_manager: - from codeweaver.providers.failover import VectorStoreFailoverManager - self.failover_manager = VectorStoreFailoverManager( - registry=self.provider_registry, - settings_map=settings_map - ) - - # Initialize and start management server - # (Always HTTP, independent of MCP transport) - from codeweaver.server.background.management import ManagementServer - - self.management_server = ManagementServer(background_state=self) - - mgmt_host = settings_map.get("server", {}).get("management_host", "127.0.0.1") - mgmt_port = settings_map.get("server", {}).get("management_port", 9329) - - await self.management_server.start(host=mgmt_host, port=mgmt_port) - - self.initialized = True - logger.info("Background services initialized") - - except Exception as e: - logger.error(f"Failed to initialize background services: {e}") - raise - - async def start_background_indexing(self) -> None: - """Start background indexing and file watching.""" - if not self.initialized or not self.indexer: - raise RuntimeError("BackgroundState not initialized") - - # Create background task for indexing + watching - task = asyncio.create_task(self._run_background_indexing()) - self.background_tasks.add(task) - task.add_done_callback(self.background_tasks.discard) - - logger.info("Background indexing started") - - async def _run_background_indexing(self) -> None: - """Background task for indexing and file watching.""" - # Implementation delegated to existing _run_background_indexing function - # from server.py (will be imported here) - from codeweaver.server.server import _run_background_indexing - - await _run_background_indexing( - state=self, - settings=self.settings, - status_display=..., # Will need to pass this through - verbose=False, - debug=False, - ) - - async def shutdown(self) -> None: - """Graceful shutdown of background services.""" - logger.info("Shutting down background services") - self.shutdown_event.set() - - # Stop management server first - if self.management_server: - await self.management_server.stop() - - # Cancel all background tasks - for task in self.background_tasks: - if not task.done(): - task.cancel() - - # Wait for tasks to complete (with timeout) - if self.background_tasks: - try: - await asyncio.wait_for( - asyncio.gather(*self.background_tasks, return_exceptions=True), - timeout=7.0 - ) - except asyncio.TimeoutError: - logger.warning("Some background tasks did not stop within 7 seconds") - - logger.info("Background services shut down") - - -# Global state reference (same pattern as before) -_state: BackgroundState | None = None - - -def get_background_state() -> BackgroundState: - """Get the current background state.""" - global _state - if _state is None: - raise RuntimeError( - "BackgroundState has not been initialized yet. " - "Ensure the server is properly set up before accessing the state." - ) - return _state -``` - -### 1.2 Create Management Server - -**File**: `src/codeweaver/server/background/management.py` (NEW) - -```python -""" -Management HTTP server for observability and monitoring. - -Runs independently of MCP transport choice (stdio or HTTP). -Provides health, stats, metrics, and settings endpoints. -""" - -from __future__ import annotations - -import asyncio -from typing import TYPE_CHECKING - -import uvicorn -from starlette.applications import Starlette -from starlette.routing import Route - -from codeweaver.common.logging import get_logger - -if TYPE_CHECKING: - from codeweaver.server.background.state import BackgroundState - -logger = get_logger(__name__) - - -class ManagementServer: - """ - HTTP server for management endpoints. - - Always runs on HTTP (port 9329), independent of MCP transport. - Provides observability endpoints for monitoring and debugging. - - Reuses existing endpoint handlers from app_bindings.py. - """ - - def __init__(self, background_state: BackgroundState): - """ - Initialize management server. - - Args: - background_state: BackgroundState instance for accessing services - """ - self.background_state = background_state - self.server: uvicorn.Server | None = None - self.server_task: asyncio.Task | None = None - - def create_app(self) -> Starlette: - """ - Create Starlette app with management routes. - - Routes are conditionally registered based on endpoint_settings - (matching pattern from app_bindings.py). - - IMPORTANT: Reuses existing handlers from app_bindings.py. - """ - from codeweaver.config.settings import get_settings_map - from codeweaver.server.app_bindings import ( - health, - stats_info, - version_info, - settings_info, - state_info, - favicon, - ) - - settings_map = get_settings_map() - endpoint_settings = settings_map.get("endpoints", {}) - - routes = [ - # Always register favicon (browsers always request it) - Route("/favicon.ico", favicon, methods=["GET"], include_in_schema=False) - ] - - # Conditional endpoints (matching app_bindings.py pattern) - if endpoint_settings.get("enable_health", True): - routes.append(Route("/health", health, methods=["GET"])) - - if endpoint_settings.get("enable_status", True): - # Note: status endpoint needs to be created in app_bindings.py - # For now, reuse health endpoint - routes.append(Route("/status", health, methods=["GET"])) - - if endpoint_settings.get("enable_metrics", True): - routes.append(Route("/metrics", stats_info, methods=["GET"])) - - if endpoint_settings.get("enable_version", True): - routes.append(Route("/version", version_info, methods=["GET"])) - - if endpoint_settings.get("enable_settings", True): - routes.append(Route("/settings", settings_info, methods=["GET"])) - - if endpoint_settings.get("enable_state", True): - routes.append(Route("/state", state_info, methods=["GET"])) - - app = Starlette(routes=routes) - - # Attach background state to app for handlers to access - # Handlers use request.app.state or get_state() global - app.state.background = self.background_state - - return app - - async def start(self, host: str = "127.0.0.1", port: int = 9329): - """ - Start management server. - - Args: - host: Server host (default: 127.0.0.1) - port: Server port (default: 9329) - """ - logger.info(f"Starting management server on {host}:{port}") - - app = self.create_app() - - config = uvicorn.Config( - app, - host=host, - port=port, - log_level="warning", # Quiet logs for management server - access_log=False # Use our own logging via @timed_http - ) - - self.server = uvicorn.Server(config) - - # Run in background task - self.server_task = asyncio.create_task(self.server.serve()) - - logger.info(f"Management server ready at http://{host}:{port}") - - async def stop(self): - """Stop management server gracefully.""" - if self.server: - logger.info("Stopping management server") - self.server.should_exit = True - - if self.server_task: - try: - await asyncio.wait_for(self.server_task, timeout=5.0) - except asyncio.TimeoutError: - logger.warning("Management server did not stop within 5 seconds") - - logger.info("Management server stopped") -``` - -### 1.3 Update Lifespan Management - -**File**: `src/codeweaver/server/lifespan.py` (NEW - extracted from server.py) - -```python -""" -Starlette lifespan integration for background services. - -Manages startup/shutdown of background services using Starlette's -AsyncExitStack pattern. -""" - -from __future__ import annotations - -from contextlib import asynccontextmanager -from typing import TYPE_CHECKING, AsyncIterator - -from codeweaver.common.logging import get_logger -from codeweaver.server.background.state import BackgroundState - -if TYPE_CHECKING: - from fastmcp import FastMCP - from codeweaver.config.settings import CodeWeaverSettings - from codeweaver.common.statistics import SessionStatistics - -logger = get_logger(__name__) - - -@asynccontextmanager -async def combined_lifespan( - app: FastMCP, - settings: CodeWeaverSettings | None = None, - statistics: SessionStatistics | None = None, - *, - verbose: bool = False, - debug: bool = False, -) -> AsyncIterator[None]: - """ - Unified lifespan context manager for background services + MCP server. - - This replaces the old lifespan() function in server.py. - Manages both background services and MCP server lifecycle. - - Args: - app: FastMCP application instance - settings: Configuration settings - statistics: Session statistics instance - verbose: Enable verbose logging - debug: Enable debug logging - """ - from codeweaver.cli.ui import StatusDisplay - from codeweaver.config.settings import get_settings - from codeweaver.common.utils import get_project_path - from codeweaver.core.types.sentinel import Unset - - # Create StatusDisplay for clean user-facing output - status_display = StatusDisplay() - - # Print clean header - server_host = getattr(app, "host", "127.0.0.1") if hasattr(app, "host") else "127.0.0.1" - server_port = getattr(app, "port", 9328) if hasattr(app, "port") else 9328 - status_display.print_header(host=server_host, port=server_port) - - if verbose or debug: - logger.info("Entering combined lifespan context manager...") - - # Load settings if not provided - if settings is None: - settings = get_settings() - if isinstance(settings.project_path, Unset): - settings.project_path = get_project_path() - - # Initialize BackgroundState (formerly AppState) - # This is the same initialization as before, just renamed - from codeweaver.server.server import _initialize_app_state - - # _initialize_app_state returns BackgroundState now - background_state = _initialize_app_state(app, settings, statistics) - - # Store in app.state for access via Context - app.state.background = background_state - - indexing_task = None - - try: - if verbose or debug: - logger.info("Initializing background services...") - - # Initialize background services - await background_state.initialize() - - # Start background indexing task - from codeweaver.server.server import _run_background_indexing - - indexing_task = asyncio.create_task( - _run_background_indexing( - background_state, settings, status_display, - verbose=verbose, debug=debug - ) - ) - - # Perform health checks and display results - status_display.print_step("Health checks...") - - if background_state.health_service: - health_response = await background_state.health_service.get_health_response() - - # Vector store health with degraded handling - vs_status = health_response.services.vector_store.status - status_display.print_health_check("Vector store (Qdrant)", vs_status) - - # Show helpful message for degraded/down vector store - if vs_status in ("down", "degraded") and not (verbose or debug): - status_display.console.print( - " [dim]Unable to connect. Continuing with sparse-only search.[/dim]" - ) - status_display.console.print( - " [dim]To enable semantic search: docker run -p 6333:6333 qdrant/qdrant[/dim]" - ) - - # Embeddings health - status_display.print_health_check( - "Embeddings (Voyage AI)", - health_response.services.embedding_provider.status, - model=health_response.services.embedding_provider.model, - ) - - # Sparse embeddings health - status_display.print_health_check( - f"Sparse embeddings ({health_response.services.sparse_embedding.provider})", - health_response.services.sparse_embedding.status, - ) - - status_display.print_ready() - - if verbose or debug: - logger.info("Lifespan start actions complete, server initialized.") - - background_state.initialized = True - - # Server runs here - yield - - except Exception: - background_state.initialized = False - raise - finally: - # Cleanup - from codeweaver.server.server import _cleanup_state - - await _cleanup_state(background_state, indexing_task, status_display, verbose=verbose or debug) -``` - -### 1.4 Update MCP Server Integration - -**File**: `src/codeweaver/server/mcp_server.py` (UPDATE) - -```python -""" -MCP server with background services integration. - -Separates protocol layer (FastMCP) from background services layer. - -Note: Observability endpoints (/health, /status, /metrics, etc.) are on -the management server (port 9329), not here. This keeps the MCP layer -focused on protocol and the ONE TOOL principle. -""" - -from fastmcp import FastMCP, Context - -from codeweaver.common.logging import get_logger -from codeweaver.server.lifespan import combined_lifespan -from codeweaver.middleware.statistics import StatisticsMiddleware - -logger = get_logger(__name__) - - -# Create FastMCP app -mcp = FastMCP("CodeWeaver", version="0.1.0-alpha.2") - - -# Register FastMCP middleware (NOT Starlette middleware) -# StatisticsMiddleware already handles all telemetry correctly -mcp.add_middleware(StatisticsMiddleware()) - - -@mcp.tool() -async def find_code( - query: str, - intent: str | None = None, - *, - token_limit: int = 30000, - focus_languages: tuple[str, ...] | None = None, - context: Context, # FastMCP automatically injects this -) -> dict: - """ - Search codebase using semantic similarity. - - This is the ONE TOOL exposed to agents (Constitutional Principle I). - - Args: - query: Natural language query or code pattern - intent: Optional intent for specialized search behavior - token_limit: Maximum tokens in response (default: 30000) - focus_languages: Optional language filter tuple - context: MCP context (injected by FastMCP) - - Returns: - FindCodeResponseSummary as dict with: - - results: List[SearchResult] with file paths, line numbers, snippets - - total: Total results found - - took_ms: Query execution time - - metadata: Search metadata (intent, filters, etc.) - """ - # Import actual implementation from app_bindings.py - # (app_bindings.find_code_tool uses get_state() global to access BackgroundState) - from codeweaver.server.app_bindings import find_code_tool - - # Delegate to actual implementation - result = await find_code_tool( - query=query, - intent=intent, - token_limit=token_limit, - focus_languages=focus_languages, - context=context - ) - - # Return as dict (FindCodeResponseSummary.model_dump()) - return result.model_dump() if hasattr(result, 'model_dump') else result - - -# Set lifespan (FastMCP will call this) -mcp._lifespan = combined_lifespan - - -# Note: Observability endpoints are on the management server (port 9329): -# - GET /health - Health check with indexing status -# - GET /status - Indexing and failover status -# - GET /metrics - Timing statistics and token metrics -# - GET /version - Version information -# - GET /settings - Configuration (redacted) -# - GET /state - Internal state -``` - -**Key Changes**: -1. βœ… **Kept**: FastMCP `StatisticsMiddleware` for call timing -2. βœ… **Added**: Context injection for accessing `BackgroundState` -3. βœ… **Simplified**: Direct delegation to `app_bindings.find_code_tool` - ---- - -## Phase 2: Configuration Updates (Week 2) - -### 2.1 Update ServerSettings - -**Rationale**: Background services are server lifecycle concerns, not indexer-specific. - -**File**: `src/codeweaver/config/server.py` (UPDATE) - -```python -""" -Server configuration (updated for background services architecture). -""" - -from pathlib import Path -from typing import Annotated, Literal - -from codeweaver.core.types.models import BasedModel, FROZEN_BASEDMODEL_CONFIG -from codeweaver.core.types.sentinel import UNSET, UnsetType - - -class ServerSettings(BasedModel): - """ - MCP server settings. - - Includes MCP server, management server, and background service configuration. - """ - - model_config = FROZEN_BASEDMODEL_CONFIG - - # MCP Server (HTTP mode only) - host: str = "127.0.0.1" - """MCP server host (use 127.0.0.1 for localhost, 0.0.0.0 for public).""" - - port: int = 9328 - """MCP server port (avoid Qdrant ports 6333-6334).""" - - transport: Literal["streamable-http", "stdio"] = "streamable-http" - """ - MCP transport protocol. - - - 'streamable-http': HTTP with SSE - recommended - - 'stdio': Standard IO transport - """ - - # Management Server (Always HTTP) - management_host: str = "127.0.0.1" - """Management server host (independent of MCP transport).""" - - management_port: int = 9329 - """Management server port (always HTTP, for health/stats/metrics).""" - - - # Existing telemetry keys definition -``` - -**File**: `config.toml` (UPDATE) - -```toml -[server] -# MCP server (HTTP mode - port 9328) -host = "127.0.0.1" -port = 9328 -transport = "streamable-http" - -# Management server (always HTTP - port 9329) -# Available regardless of MCP transport (stdio or HTTP) -management_host = "127.0.0.1" -management_port = 9329 - -``` - -**Immutability Pattern**: - -```python -# βœ… CORRECT: Immutable read-only access (95% of cases) -from codeweaver.config.settings import get_settings_map - -settings_map = get_settings_map() # Returns DictView[CodeWeaverSettingsDict] -auto_index = settings_map["server"]["auto_index_on_startup"] # bool - -# ⚠️ Only when need property/method access: -from codeweaver.config.settings import get_settings - -settings = get_settings() # Returns CodeWeaverSettings (BasedModel) -auto_index = settings.server.auto_index_on_startup # Property access -``` - ---- - -## Phase 3: CLI Commands (Week 2) - -### 3.1 Background Services Management - -**File**: `src/codeweaver/cli/commands/services.py` (NEW) - -```python -""" -Background services management commands. - -Manages CodeWeaver's background services (indexing, file watching, telemetry). -""" - -from pathlib import Path -from typing import Annotated - -import asyncio -import cyclopts -from rich.console import Console - -from codeweaver.common.logging import get_logger -from codeweaver.config.settings import get_settings_map - -logger = get_logger(__name__) -console = Console() - - -async def is_services_running() -> bool: - """Check if background services are running via management server.""" - import httpx - - settings_map = get_settings_map() - mgmt_host = settings_map.get("server", {}).get("management_host", "127.0.0.1") - mgmt_port = settings_map.get("server", {}).get("management_port", 9329) - - try: - async with httpx.AsyncClient() as client: - response = await client.get( - f"http://{mgmt_host}:{mgmt_port}/health", - timeout=2.0 - ) - return response.status_code == 200 - except (httpx.ConnectError, httpx.TimeoutException): - return False - - -async def start_background_services() -> None: - """Start background services (indexer, watcher, health, management server).""" - from codeweaver.server.background.state import BackgroundState - - # Initialize state (same pattern as server startup) - background_state = BackgroundState(...) # Initialize with proper params - - await background_state.initialize() - await background_state.start_background_indexing() - - # Keep services running (until interrupted) - try: - await background_state.shutdown_event.wait() - except KeyboardInterrupt: - console.print("\n[yellow]Shutting down background services...[/yellow]") - finally: - await background_state.shutdown() - - -async def stop_background_services() -> None: - """Stop background services gracefully.""" - import signal - import os - - # Use signal-based shutdown (more secure than HTTP endpoint) - os.kill(os.getpid(), signal.SIGTERM) - - -@cyclopts.command -def start( - config: Annotated[ - Path | None, - cyclopts.Parameter(help="Path to CodeWeaver configuration file") - ] = None, - project: Annotated[ - Path | None, - cyclopts.Parameter(help="Path to project directory") - ] = None, -) -> None: - """ - Start CodeWeaver background services. - - Starts: - - Indexer (semantic search engine) - - FileWatcher (real-time index updates) - - HealthService (system monitoring) - - Statistics (telemetry collection) - - Management server (HTTP on port 9329) - - Background services run independently of the MCP server. - The MCP server will auto-start these if needed. - - Management endpoints available at http://127.0.0.1:9329: - - /health - Health check - - /status - Indexing status - - /metrics - Statistics and metrics - - /version - Version information - """ - if asyncio.run(is_services_running()): - console.print("[yellow]Background services already running[/yellow]") - console.print("[dim]Management server: http://127.0.0.1:9329[/dim]") - return - - console.print("[green]Starting CodeWeaver background services...[/green]") - console.print("[dim]Press Ctrl+C to stop[/dim]") - - asyncio.run(start_background_services()) - - -@cyclopts.command -def stop() -> None: - """ - Stop CodeWeaver background services. - - Gracefully shuts down all background services using SIGTERM. - """ - if not asyncio.run(is_services_running()): - console.print("[yellow]Background services not running[/yellow]") - return - - console.print("[yellow]Stopping background services...[/yellow]") - asyncio.run(stop_background_services()) -``` - -### 3.2 Update Server Command - -**File**: `src/codeweaver/cli/commands/server.py` (UPDATE) - -```python -""" -MCP server commands (updated for background services architecture). -""" - -from pathlib import Path -from typing import Annotated, Literal - -import asyncio -import sys -import cyclopts -from rich.console import Console - -from codeweaver.common.logging import get_logger -from codeweaver.config.settings import get_settings_map - -logger = get_logger(__name__) -console = Console() - - -@cyclopts.command -def server( - transport: Annotated[ - Literal["streamable-http", "stdio"], - cyclopts.Parameter(help="MCP transport protocol") - ] = "streamable-http", - host: Annotated[ - str | None, - cyclopts.Parameter(help="Server host (HTTP only)") - ] = None, - port: Annotated[ - int | None, - cyclopts.Parameter(help="Server port (HTTP only)") - ] = None, - no_auto_start: Annotated[ - bool, - cyclopts.Parameter(help="Don't auto-start background services") - ] = False, -) -> None: - """ - Start CodeWeaver MCP server. - - Automatically starts background services if not already running - (disable with --no-auto-start). - - Transport modes: - - streamable-http (default): HTTP-based transport - - stdio: Standard I/O transport (launched per-session by MCP clients) - - Background services (indexing, file watching, telemetry) will auto-start - unless already running or --no-auto-start is specified. - """ - settings_map = get_settings_map() - - # Check and potentially start background services - if not no_auto_start: - from codeweaver.cli.commands.services import is_services_running, start_background_services - - if not asyncio.run(is_services_running()): - console.print("[dim]Background services not running, starting...[/dim]") - - # Start services in background thread - import threading - services_thread = threading.Thread( - target=lambda: asyncio.run(start_background_services()), - daemon=True - ) - services_thread.start() - - # Wait for initialization - import time - time.sleep(2) - console.print("[green]βœ“[/green] Background services started") - else: - console.print("[dim]Background services already running[/dim]") - - # Start MCP server - if transport == "streamable-http": - final_host = host or settings_map["server"]["host"] - final_port = port or settings_map["server"]["port"] - - console.print(f"[green]Starting MCP server on {final_host}:{final_port}[/green]") - console.print(f"[dim]Management server: http://127.0.0.1:9329[/dim]") - - from codeweaver.server.mcp_server import mcp - - mcp.run( - transport="sse", # FastMCP >= 2.13.1 uses "sse" for streamable-http - host=final_host, - port=final_port - ) - - elif transport == "stdio": - console.print("[green]Starting MCP stdio server[/green]", file=sys.stderr) - console.print("[dim]Background services auto-started[/dim]", file=sys.stderr) - - from codeweaver.server.mcp_server import mcp - - mcp.run(transport="stdio") - - else: - console.print(f"[red]Unknown transport: {transport}[/red]", err=True) - raise SystemExit(1) -``` - ---- - -## Phase 4: Testing & Validation (Week 3) - -### 4.1 Background Services Tests - -**File**: `tests/integration/test_background_services.py` - -```python -""" -Integration tests for background services architecture. -""" - -import pytest -import asyncio - -from codeweaver.server.background.state import BackgroundState - - -@pytest.mark.asyncio -async def test_background_state_lifecycle(): - """Test background state initialization and shutdown.""" - # Initialize state (formerly AppState) - state = BackgroundState(...) # Proper initialization - - assert not state.initialized - - # Initialize services - await state.initialize() - assert state.initialized - assert state.indexer is not None - assert state.statistics is not None - assert state.management_server is not None - - # Start background indexing - await state.start_background_indexing() - assert len(state.background_tasks) > 0 - - # Shutdown - await state.shutdown() - assert all(task.done() for task in state.background_tasks) - - -@pytest.mark.asyncio -async def test_find_code_via_mcp_context(): - """Test find_code tool accesses state via Context injection.""" - from codeweaver.server.mcp_server import mcp - from fastmcp.testing import get_session_for_test - - async with get_session_for_test(mcp) as session: - # Call find_code tool - result = await session.call_tool( - "find_code", - arguments={ - "query": "semantic search implementation", - "token_limit": 10000 - } - ) - - # Should return FindCodeResponseSummary structure - assert "results" in result - assert "total" in result - assert "took_ms" in result - - -@pytest.mark.asyncio -async def test_statistics_middleware_telemetry(): - """Verify StatisticsMiddleware captures telemetry correctly.""" - from codeweaver.middleware.statistics import StatisticsMiddleware - from codeweaver.common.statistics import SessionStatistics - - stats = SessionStatistics() - middleware = StatisticsMiddleware(statistics=stats) - - # Verify middleware is FastMCP middleware (not Starlette) - from fastmcp.server.middleware.middleware import Middleware - assert isinstance(middleware, Middleware) - - # Telemetry tracking verified - assert stats.total_requests == 0 - - # After tool call (simulated) - # stats.total_requests should increment - # Duration should be tracked - - -@pytest.mark.asyncio -async def test_management_server_independence(): - """Verify management server works in stdio mode.""" - import httpx - - # Start background services - state = BackgroundState(...) - await state.initialize() - - # Management server should be accessible even in stdio mode - async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:9329/health") - assert response.status_code == 200 - - await state.shutdown() - - -@pytest.mark.asyncio -async def test_no_reindex_tool_exposed(): - """Verify reindex is NOT exposed as MCP tool (Constitutional compliance).""" - from codeweaver.server.mcp_server import mcp - - # Get all registered tools - tools = [tool.name for tool in mcp.list_tools()] - - # Should only have find_code - assert "find_code" in tools - assert "reindex" not in tools - assert "get_index_status" not in tools - - # ONE TOOL principle - assert len([t for t in tools if t.startswith("find_")]) == 1 -``` - ---- - -## Phase 5: Documentation (Week 4) - -### 5.1 Architecture Documentation - -**File**: `docs/architecture/background-services.md` - -```markdown -# Background Services Architecture - -## Overview - -CodeWeaver separates concerns between: -- **Protocol Layer**: FastMCP handles MCP protocol (HTTP or stdio) -- **Background Services Layer**: Indexing, file watching, health monitoring -- **Provider Layer**: Vector stores, embedders, indexers (via ProviderRegistry) - -## Key Architecture Changes - -### State Management - -**Before (alpha.1)**: -- Single `AppState` class managed everything -- Tightly coupled to MCP server lifecycle - -**After (alpha.2)**: -- `BackgroundState` (renamed from `AppState`) manages background services -- Lightweight MCP server accesses `BackgroundState` via Context injection -- Clear separation: protocol β‰  business logic - -### Middleware Architecture - -**FastMCP Middleware** (Used): -- `StatisticsMiddleware`: Captures ALL MCP operation telemetry -- Works in both stdio and HTTP transports -- Hooks: `on_call_tool`, `on_read_resource`, `on_get_prompt`, etc. - -**Starlette Middleware** (NOT Used for MCP concerns): -- Only relevant for HTTP-level concerns (CORS, compression, etc.) -- Does NOT see MCP protocol operations in stdio mode -- NOT used for state passing or telemetry - -### Information Flow - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ find_code Tool β”‚ -β”‚ - Receives query from agent β”‚ -β”‚ - Accesses BackgroundState via global getter β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ get_background_state() (global function) -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ BackgroundState β”‚ -β”‚ - Indexer (search engine) β”‚ -β”‚ - ProviderRegistry (services) β”‚ -β”‚ - HealthService (monitoring) β”‚ -β”‚ - SessionStatistics (telemetry sink) β”‚ -β”‚ - FailoverManager (resilience) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ Uses providers -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Provider Layer β”‚ -β”‚ - VectorStore (Qdrant) β”‚ -β”‚ - Embedder (Voyage AI) β”‚ -β”‚ - Sparse Embedder (SPLADE) β”‚ -β”‚ - Reranker β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -Telemetry Flow (parallel): -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ StatisticsMiddleware.on_call_tool() β”‚ -β”‚ - Wraps ALL tool calls β”‚ -β”‚ - Captures timing, success/failure, metadata β”‚ -β”‚ - Updates SessionStatistics β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -### Management Server - -**Port 9329** (Always HTTP): -- Independent of MCP transport choice -- Endpoints: `/health`, `/metrics`, `/version`, `/settings`, `/state` -- Reuses handlers from `app_bindings.py` -- Accessible in both stdio and HTTP MCP modes - -**Port 9328** (MCP Server, HTTP mode only): -- MCP protocol endpoint -- ONE TOOL: `find_code` -- No custom HTTP endpoints - -## Constitutional Compliance - -**ONE TOOL for agents**: `find_code()` - -Reindexing and status operations are **intentionally not exposed** as MCP tools. -These are CLI-only operations (via `cw index` commands) to prevent agents from -triggering expensive reindex operations. - -Rationale: "We kind of intentionally made it hard to order a reindex... It's like -the hard reset on an iPhone -- you only need to know how to do it when you really need it." - -## Migration from alpha.1 - -### Code Changes - -```python -# BEFORE (alpha.1) -from codeweaver.server.server import get_state - -state = get_state() # Returns AppState -indexer = state.indexer - -# AFTER (alpha.2) -from codeweaver.server.background.state import get_background_state - -# Use global getter (same pattern, just renamed) -background_state = get_background_state() # Returns BackgroundState -indexer = background_state.indexer - -# Note: DI framework planned for future will replace global getter pattern -``` - -### Configuration Changes - -```toml -# BEFORE (alpha.1) -[server] -host = "127.0.0.1" -port = 9328 - -# AFTER (alpha.2) -[server] -# MCP server -host = "127.0.0.1" -port = 9328 -transport = "streamable-http" - -# Management server (NEW) -management_host = "127.0.0.1" -management_port = 9329 - -# Background services (NEW) -auto_index_on_startup = true -file_watching_enabled = true -``` - -### CLI Changes - -```bash -# BEFORE (alpha.1) -codeweaver server start - -# AFTER (alpha.2) -# Option 1: Start just MCP server (auto-starts services) -codeweaver server - -# Option 2: Start services separately -codeweaver start # Start background services -codeweaver server --no-auto-start # Start MCP server without auto-start - -# Option 3: Check status -codeweaver status # Shows services + index -codeweaver status --services # Shows just services -codeweaver status --index # Shows just index -``` - -## Deployment Models - -### Development (Single Process) -```bash -codeweaver server start -``` - -### Production (Docker) -```yaml -services: - codeweaver: - image: codeweaver:latest - ports: - - "9328:9328" # MCP server - - "9329:9329" # Management server - environment: - - CODEWEAVER_PROJECT_PATH=/workspace -``` - -### Production (Systemd - Linux) -```ini -[Unit] -Description=CodeWeaver MCP Server -After=network.target - -[Service] -Type=simple -ExecStart=/opt/codeweaver/.venv/bin/codeweaver server -Restart=always - -[Install] -WantedBy=multi-user.target -``` -``` - ---- - -## Migration Checklist (Revised) - -### Development Tasks - -**Phase 1: State Management** -- [ ] Rename `AppState` β†’ `BackgroundState` in `server/server.py` -- [ ] Move to `src/codeweaver/server/background/state.py` -- [ ] Add `management_server`, `background_tasks`, `shutdown_event` fields -- [ ] Update all imports (`get_state` β†’ `get_background_state`) -- [ ] Add `initialize()` and `shutdown()` methods - -**Phase 2: Management Server** -- [ ] Create `ManagementServer` class in `server/background/management.py` -- [ ] Reuse existing endpoint handlers from `app_bindings.py` -- [ ] Test endpoints with background state access - -**Phase 3: Lifespan** -- [ ] Create `combined_lifespan()` in `server/lifespan.py` -- [ ] Initialize `BackgroundState` -- [ ] Start background indexing -- [ ] Start management server -- [ ] Attach to `app.state.background` - -**Phase 4: MCP Server** -- [ ] Update `mcp_server.py` to use `combined_lifespan` -- [ ] Update `find_code` to access state via Context injection -- [ ] Remove any Starlette middleware for state passing -- [ ] Keep FastMCP `StatisticsMiddleware` for telemetry - -**Phase 5: Configuration** -- [ ] Update `ServerSettings` with management ports and background flags -- [ ] Update `config.toml` schema -- [ ] Test immutability patterns with `get_settings_map()` - -**Phase 6: CLI** -- [ ] Implement `cw start` command -- [ ] Implement `cw stop` command (signal-based) -- [ ] Update `cw server` (auto-start services) -- [ ] Update `cw status` (services + index) -- [ ] Register commands in `cli/main.py` - -### Testing Tasks - -- [ ] Test background services lifecycle -- [ ] Test Context injection in `find_code` -- [ ] Verify `StatisticsMiddleware` telemetry capture -- [ ] Test management server in stdio mode -- [ ] Test auto-start behavior -- [ ] Test graceful shutdown -- [ ] Verify ONE TOOL principle (only find_code) -- [ ] Test health endpoints -- [ ] Cross-platform tests (Linux, macOS, Windows, WSL) - -### Documentation Tasks - -- [ ] Update README with new architecture -- [ ] Document middleware patterns (FastMCP vs Starlette) -- [ ] Document Context injection pattern -- [ ] Document management server separation -- [ ] Document CLI commands -- [ ] Create migration guide from alpha.1 - ---- - -## Risk Mitigation - -### Middleware Misunderstanding (CRITICAL) - -**Risk**: Using Starlette middleware for MCP concerns won't work in stdio mode - -**Mitigation**: -- βœ… Use FastMCP middleware (`StatisticsMiddleware`) for telemetry -- βœ… Use Context injection for state access -- βœ… Document difference clearly -- βœ… Integration tests verify both transports - -### State Access Pattern - -**Risk**: Developers might not know how to access `BackgroundState` - -**Mitigation**: -- βœ… Document Context injection pattern clearly -- βœ… Provide examples in docstrings -- βœ… Create helper functions if needed -- βœ… Migration guide shows before/after patterns - -### Port Conflicts - -**Risk**: Port 9329 conflicts with other services - -**Mitigation**: -- βœ… Configurable via `management_port` setting -- βœ… Default 9329 unlikely to conflict -- βœ… Document port usage clearly -- βœ… Startup error handling for port conflicts - ---- - -## Success Metrics - -### Functional Requirements - -- [ ] **Constitutional Compliance**: ONE TOOL (`find_code`) exposed to agents -- [ ] **Pattern Fidelity**: Uses BasedModel, get_settings_map(), ProviderRegistry -- [ ] **Cross-Platform**: Works on Linux, macOS, Windows, WSL -- [ ] **Lifecycle Management**: Graceful startup/shutdown via Starlette lifespan -- [ ] **Middleware Correctness**: FastMCP middleware for MCP, not Starlette -- [ ] **Context Injection**: State access via Context parameter -- [ ] **Management Server**: Independent HTTP server on port 9329 - -### Quality Requirements - -- [ ] All tests pass on all platforms -- [ ] No regressions from alpha.1 -- [ ] Telemetry via `StatisticsMiddleware` works correctly -- [ ] Health checks operational -- [ ] Documentation complete and accurate - -### Performance Requirements - -- [ ] Startup time: < 30s for initial indexing -- [ ] File watching: Real-time updates (< 2s latency) -- [ ] Memory usage: < 500MB for medium projects (50k LOC) -- [ ] Query latency: < 100ms p50, < 500ms p95 - ---- - -## Timeline - -**Week 1**: State Management & Lifespan -- Days 1-2: Rename AppState β†’ BackgroundState, add new fields -- Days 3-4: Create ManagementServer, update lifespan -- Day 5: Integration testing - -**Week 2**: Configuration & CLI -- Days 1-2: Update ServerSettings, test immutability -- Days 3-4: CLI commands (start, stop, server, status) -- Day 5: End-to-end CLI testing - -**Week 3**: Testing & Polish -- Days 1-2: Integration tests for all scenarios -- Days 3-5: Cross-platform testing, performance benchmarks - -**Week 4**: Documentation & Release -- Days 1-2: Architecture documentation, migration guide -- Days 3-4: User documentation, examples -- Day 5: Alpha.2 release - ---- - -## Conclusion - -This implementation plan: - -1. **Repurposes existing AppState** as BackgroundState (minimal disruption) -2. **Uses FastMCP middleware correctly** for telemetry (not Starlette) -3. **Separates management server** (port 9329) from MCP server (port 9328) -4. **Maintains Constitutional compliance** (ONE TOOL principle) -5. **Provides clear migration path** from alpha.1 - -**Critical Success Factors**: -- Understanding FastMCP middleware vs Starlette middleware -- Using Context injection for state access -- Repurposing AppState rather than creating new class -- Testing both stdio and HTTP transports thoroughly diff --git a/claudedocs/phase-1-implementation-summary.md b/claudedocs/phase-1-implementation-summary.md deleted file mode 100644 index 1be75c03..00000000 --- a/claudedocs/phase-1-implementation-summary.md +++ /dev/null @@ -1,213 +0,0 @@ - - -# Phase 1 Implementation Summary - -**Date**: 2025-11-28 -**Status**: βœ… COMPLETE -**Focus**: Background Services Extraction & Foundation - -## Implementation Overview - -Phase 1 successfully extracted the background services architecture as described in the implementation plan, creating a clean separation between protocol layer and background services while maintaining full backward compatibility. - -## Files Created - -### 1. Background Services Module -- **`src/codeweaver/server/background/__init__.py`** - Module initialization -- **`src/codeweaver/server/background/state.py`** - BackgroundState class (formerly AppState) -- **`src/codeweaver/server/background/management.py`** - ManagementServer for observability endpoints -- **`src/codeweaver/server/lifespan.py`** - Combined lifespan context manager - -## Files Modified - -### 1. Configuration Settings -- **`src/codeweaver/config/settings.py`** - Added management server configuration: - - `management_host` (default: "127.0.0.1") - - `management_port` (default: 9329) - - `management_server_enabled` (default: False - deferred to later phases) - - Background service flags: - - `auto_index_on_startup` (default: True) - - `file_watching_enabled` (default: True) - - `health_check_interval_seconds` (default: 30) - - `statistics_enabled` (default: True) - -### 2. Server Module -- **`src/codeweaver/server/server.py`** - Added backward compatibility: - - `BackgroundState = AppState` (alias) - - `get_background_state = get_state` (alias) - - Updated `__all__` exports to include new names - -## Key Features Implemented - -### BackgroundState Class -βœ… Renamed from AppState with same functionality -βœ… Added `management_server` field (placeholder for Phase 1.2) -βœ… Added `background_tasks` set for task tracking -βœ… Added `shutdown_event` for coordinated shutdown -βœ… Implemented `initialize()` method for service initialization -βœ… Implemented `shutdown()` method for graceful cleanup -βœ… Maintained all existing fields and computed properties - -### ManagementServer Class -βœ… Independent HTTP server (port 9329) -βœ… Works independently of MCP transport (stdio or HTTP) -βœ… Reuses existing endpoint handlers from `app_bindings.py`: - - `/health` - Health check endpoint - - `/status` - Status endpoint (alias to health) - - `/metrics` - Statistics endpoint - - `/version` - Version information - - `/settings` - Configuration (redacted) - - `/state` - Internal state - - `/favicon.ico` - Favicon handler -βœ… Conditional endpoint registration based on settings -βœ… Graceful startup and shutdown - -### Combined Lifespan -βœ… Unified lifespan context manager -βœ… Manages BackgroundState initialization -βœ… Integrates with existing health checks -βœ… Supports verbose and debug logging -βœ… Maintains compatibility with existing server.py patterns - -## Backward Compatibility - -All changes maintain 100% backward compatibility: -- βœ… `AppState` still available and functional -- βœ… `get_state()` still works -- βœ… Existing code continues to work without modifications -- βœ… All imports validated successfully -- βœ… Type aliases allow gradual migration - -## Configuration Validation - -New configuration fields added to FastMcpServerSettings: -```toml -[server] -# Management Server (Always HTTP, independent of MCP transport) -management_host = "127.0.0.1" -management_port = 9329 -management_server_enabled = false # Disabled for Phase 1 - -# Background Services -auto_index_on_startup = true -file_watching_enabled = true -health_check_interval_seconds = 30 -statistics_enabled = true -``` - -## Testing Results - -βœ… All module imports successful: -- `from codeweaver.server.background import BackgroundState, get_background_state` -- `from codeweaver.server.background.management import ManagementServer` -- `from codeweaver.server.lifespan import combined_lifespan` -- `from codeweaver.server.server import BackgroundState, get_background_state` (aliases) - -βœ… No import errors -βœ… Backward compatibility maintained - -## Next Steps (Phase 1.2 - Phase 2) - -### Phase 1.2: Enable Management Server -- [ ] Set `management_server_enabled = True` by default -- [ ] Integrate ManagementServer initialization in BackgroundState.initialize() -- [ ] Update combined_lifespan to start management server -- [ ] Test management endpoints in both stdio and HTTP modes - -### Phase 2: Configuration & CLI Updates -- [ ] Add `cw start` command for background services -- [ ] Add `cw stop` command for graceful shutdown -- [ ] Update `cw server` command with auto-start logic -- [ ] Update configuration documentation - -### Phase 3: Testing & Validation -- [ ] Integration tests for BackgroundState lifecycle -- [ ] Tests for ManagementServer endpoints -- [ ] Tests for Context injection in find_code -- [ ] Cross-platform testing (Linux, macOS, Windows, WSL) - -## Architecture Notes - -### Design Decisions - -1. **Backward Compatibility First**: Used aliases to allow gradual migration without breaking changes -2. **Management Server Disabled**: Deferred full integration to Phase 1.2 to minimize risk -3. **Same-Process Architecture**: Maintained single-process model with clear boundaries -4. **Pattern Reuse**: Leveraged existing AppState structure, just renamed and extended - -### Information Flow (Implemented) - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ MCP Protocol Layer (FastMCP) β”‚ -β”‚ - Port 9328 (HTTP mode) β”‚ -β”‚ - stdio (Standard I/O mode) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ Access via global getter -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ BackgroundState β”‚ -β”‚ - ProviderRegistry β”‚ -β”‚ - IndexerService β”‚ -β”‚ - HealthService β”‚ -β”‚ - SessionStatistics β”‚ -β”‚ - ManagementServer (placeholder) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -### Constitutional Compliance - -βœ… **ONE TOOL Principle**: Only `find_code()` exposed to agents -βœ… **Pattern Fidelity**: Uses BasedModel, get_settings_map(), immutability -βœ… **Lifecycle Management**: Starlette lifespan pattern maintained - -## Success Metrics - -βœ… Phase 1 Core Objectives: -- [x] BackgroundState class created and functional -- [x] ManagementServer class created (ready for integration) -- [x] combined_lifespan implemented -- [x] Configuration extended with management/background settings -- [x] Backward compatibility 100% maintained -- [x] All imports working correctly -- [x] Zero breaking changes - -## Risk Mitigation - -βœ… **Import Errors**: Fixed by using `logging.getLogger(__name__)` pattern -βœ… **Backward Compatibility**: Validated via type aliases and import tests -βœ… **Type Safety**: Maintained type annotations throughout -βœ… **Documentation**: Clear inline comments and docstrings - -## Implementation Quality - -**Code Quality**: ⭐⭐⭐⭐⭐ -- Clean separation of concerns -- Comprehensive type hints -- Detailed docstrings -- Pattern consistency with existing code - -**Testing**: ⭐⭐⭐⭐ -- Import validation complete -- Backward compatibility verified -- Integration tests deferred to Phase 3 - -**Documentation**: ⭐⭐⭐⭐⭐ -- Inline code documentation -- Architecture decision records -- Migration guide included in implementation plan - -## Conclusion - -Phase 1 successfully laid the foundation for background services separation. The implementation: -- Maintains complete backward compatibility -- Provides clear migration path for future phases -- Follows existing code patterns and standards -- Enables full stdio support once integrated -- Sets up proper lifecycle management - -**Ready for Phase 1.2**: Management server integration and testing. diff --git a/claudedocs/phase-2-implementation-summary.md b/claudedocs/phase-2-implementation-summary.md deleted file mode 100644 index 27b33f7d..00000000 --- a/claudedocs/phase-2-implementation-summary.md +++ /dev/null @@ -1,193 +0,0 @@ - - -# Phase 2 Implementation Summary - -**Date**: 2025-11-28 -**Status**: βœ… COMPLETE -**Focus**: Configuration Updates - -## Implementation Overview - -Phase 2 successfully updated the configuration files to include management server settings while maintaining clean separation of concerns. - -## Files Modified - -### 1. Configuration Settings -- **`src/codeweaver/config/settings.py`** - Updated FastMcpServerSettings: - - Added `management_host` (default: "127.0.0.1") - - Added `management_port` (default: 9329) - - Removed redundant background service fields (handled elsewhere) - -### 2. Example Configuration -- **`codeweaver.toml`** - Updated `[server]` section: - - Added MCP server settings (host, port, transport) - - Added management server settings (host, port) - - Maintained existing FastMCP settings - -## Configuration Structure - -### FastMcpServerSettings (settings.py) -```python -class FastMcpServerSettings(BasedModel): - # MCP Server - transport: Literal["stdio", "http", "streamable-http"] | None = "streamable-http" - host: str | None = "127.0.0.1" - port: PositiveInt | None = 9328 - - # Management Server (Always HTTP, independent of MCP transport) - management_host: str = "127.0.0.1" - management_port: PositiveInt = 9329 - - # ... existing FastMCP settings ... -``` - -### Example Config (codeweaver.toml) -```toml -[server] -# MCP Server (HTTP mode - port 9328) -host = "127.0.0.1" -port = 9328 -transport = "streamable-http" - -# Management Server (Always HTTP - port 9329) -# Available regardless of MCP transport (stdio or HTTP) -management_host = "127.0.0.1" -management_port = 9329 - -# FastMCP Settings -on_duplicate_tools = "replace" -on_duplicate_resources = "replace" -on_duplicate_prompts = "replace" -resource_prefix_format = "path" -``` - -## Key Features - -βœ… **Management Server Configuration**: -- Independent host and port settings -- Works with both stdio and HTTP MCP modes -- Clear separation from MCP server settings - -βœ… **Clean Configuration Structure**: -- Removed duplicate background service settings -- Maintained backward compatibility -- Clear comments explaining each section - -βœ… **Immutability Pattern Support**: -- Settings accessible via `get_settings_map()` (read-only) -- Settings accessible via `get_settings()` (property access) - -## Testing Results - -βœ… Configuration loads successfully: -``` -βœ“ Settings loaded successfully - MCP Server: 127.0.0.1:9328 (streamable-http) - Management Server: 127.0.0.1:9329 -``` - -βœ… No import errors -βœ… Backward compatibility maintained -βœ… All settings accessible - -## Changes from Original Plan - -**Simplified Background Services**: -- Removed `management_server_enabled` (will enable by default in Phase 1.2) -- Removed background service flags from `FastMcpServerSettings` (already defined elsewhere) -- Kept only essential management server configuration (host, port) - -This simplification: -- Avoids configuration duplication -- Maintains single source of truth for settings -- Reduces complexity while preserving functionality - -## Validation - -### Configuration Loading -```python -from codeweaver.config.settings import get_settings, get_settings_map - -settings = get_settings() -settings_map = get_settings_map() - -# Access management server settings -mgmt_host = settings.server.management_host -mgmt_port = settings.server.management_port - -# Or via immutable read-only access -mgmt_host = settings_map["server"]["management_host"] -mgmt_port = settings_map["server"]["management_port"] -``` - -### Example Usage -```python -# In BackgroundState.initialize() -from codeweaver.config.settings import get_settings_map - -settings_map = get_settings_map() -mgmt_host = settings_map["server"]["management_host"] -mgmt_port = settings_map["server"]["management_port"] - -# Initialize management server with configured settings -self.management_server = ManagementServer(background_state=self) -await self.management_server.start(host=mgmt_host, port=mgmt_port) -``` - -## Next Steps (Phase 1.2) - -### Enable Management Server -- [ ] Enable management server by default in BackgroundState.initialize() -- [ ] Start management server during lifespan startup -- [ ] Test management endpoints in stdio mode -- [ ] Test management endpoints in HTTP mode -- [ ] Validate graceful shutdown - -## Architecture Compliance - -βœ… **Pattern Fidelity**: Uses BasedModel, immutable settings -βœ… **Configuration Best Practices**: Single source of truth -βœ… **Backward Compatibility**: Existing code continues to work -βœ… **Documentation**: Clear inline comments - -## Success Metrics - -βœ… Phase 2 Core Objectives: -- [x] Management server configuration added -- [x] Example config updated -- [x] Redundant settings removed -- [x] Configuration loads successfully -- [x] Immutability pattern supported -- [x] Zero breaking changes - -## Implementation Quality - -**Code Quality**: ⭐⭐⭐⭐⭐ -- Clean configuration structure -- Clear separation of concerns -- Well-documented settings - -**Testing**: ⭐⭐⭐⭐⭐ -- Configuration loading validated -- Settings access patterns tested -- Backward compatibility verified - -**Documentation**: ⭐⭐⭐⭐⭐ -- Inline comments explain each section -- Examples show usage patterns -- Clear migration path documented - -## Conclusion - -Phase 2 successfully updated configuration files to support the management server architecture. The implementation: -- Maintains clean separation between MCP and management servers -- Provides clear configuration examples -- Supports both immutable and property access patterns -- Enables management server integration in Phase 1.2 - -**Ready for Phase 1.2**: Management server initialization and testing. diff --git a/claudedocs/phase-3-implementation-summary.md b/claudedocs/phase-3-implementation-summary.md deleted file mode 100644 index 5be11ffd..00000000 --- a/claudedocs/phase-3-implementation-summary.md +++ /dev/null @@ -1,415 +0,0 @@ - - -# Phase 3 Implementation Summary (FINAL) - -**Date**: 2025-11-28 -**Status**: βœ… COMPLETE -**Focus**: CLI Commands for Background Services Management - -## Implementation Overview - -Phase 3 successfully implemented top-level CLI commands for background services management following CodeWeaver's architecture patterns. Commands are implemented as separate top-level modules using StatusDisplay and CLIErrorHandler from codeweaver.cli.ui. - -## Files Created - -### 1. Start Command -- **`src/codeweaver/cli/commands/start.py`** (206 lines) - Top-level `cw start` command: - - Full BackgroundState initialization - - HealthService setup with proper dependencies - - Background indexing task management - - Graceful shutdown handling - - Uses StatusDisplay for all output - - CLIErrorHandler for error management - -### 2. Stop Command -- **`src/codeweaver/cli/commands/stop.py`** (87 lines) - Top-level `cw stop` command: - - Signal-based graceful shutdown (SIGTERM) - - Health check before stopping - - Uses StatusDisplay for all output - - CLIErrorHandler for error management - -## Files Modified - -### 1. CLI Main Entry Point -- **`src/codeweaver/cli/__main__.py`** - Registered start and stop commands: - - Added `app.command("codeweaver.cli.commands.start:app", name="start")` - - Added `app.command("codeweaver.cli.commands.stop:app", name="stop")` - - Removed incorrect services command registration - -### 2. Status Command Extension -- **`src/codeweaver/cli/commands/status.py`** - Extended to show background services: - - Added `get_management_url()` function - - Added `_query_management_health()` function - - Added `_display_management_status()` function - - Updated `_show_status_once()` to query both MCP and management servers - - Updated `_watch_status()` to display both statuses - - Updated section headers for clarity (MCP Server Status vs Background Services) - -## CLI Commands Structure - -### Start Command (`cw start`) - -**Usage**: -```bash -cw start [--config PATH] [--project PATH] -``` - -**Full Implementation Features**: -- βœ… Complete BackgroundState initialization with all required parameters -- βœ… HealthService initialization with proper dependencies -- βœ… Telemetry client integration -- βœ… Background indexing task management -- βœ… Graceful shutdown with cleanup -- βœ… Health check before starting (prevents duplicate instances) -- βœ… Uses StatusDisplay for all output -- βœ… CLIErrorHandler for error management - -**Example Output**: -``` -CodeWeaver start - Start Background Services - -ℹ️ Starting CodeWeaver background services... -⚠️ Press Ctrl+C to stop - -βœ“ Background services started successfully -🌐 Management server: http://127.0.0.1:9329 - -[Background indexing runs until Ctrl+C] -``` - -### Stop Command (`cw stop`) - -**Usage**: -```bash -cw stop -``` - -**Features**: -- βœ… Signal-based graceful shutdown (SIGTERM) -- βœ… Health check before stopping -- βœ… Clear user messaging -- βœ… Uses StatusDisplay for all output - -**Example Output**: -``` -CodeWeaver stop - Stop Background Services - -ℹ️ Stopping background services... -[Sends SIGTERM for graceful shutdown] -``` - -### Status Command (`cw status`) - Extended - -**Usage**: -```bash -cw status [--verbose] [--watch] [--watch-interval N] -``` - -**New Features**: -- βœ… Queries management server health (port 9329) -- βœ… Shows background services status -- βœ… Shows MCP server status (port 9328) -- βœ… Clear section headers distinguish services - -**Example Output** (No Services Running): -``` -CodeWeaver status - CodeWeaver Runtime Status - -Background Services -⚠️ Background services not running -ℹ️ To start background services: 'cw start' - -MCP Server Status -βœ— Error: MCP server offline at http://127.0.0.1:9328 -ℹ️ The CodeWeaver server is not running... -ℹ️ To start the server, run: 'cw server' -``` - -**Example Output** (Services Running): -``` -CodeWeaver status - CodeWeaver Runtime Status - -Background Services -βœ“ Background services running -🌐 Management server: http://127.0.0.1:9329 - -MCP Server Status -βœ“ MCP server online - Uptime: 1h 23m 45s - -Indexing Status -... -``` - -## Architecture Compliance - -βœ… **Top-Level Commands**: Each command (`start`, `stop`) is its own module -βœ… **StatusDisplay Pattern**: All commands use StatusDisplay for output -βœ… **CLIErrorHandler Pattern**: All commands use CLIErrorHandler for errors -βœ… **CLI Patterns**: Follows existing cyclopts structure -βœ… **Initialization**: Proper BackgroundState and HealthService setup -βœ… **Error Handling**: Graceful degradation and comprehensive error handling -βœ… **Documentation**: Comprehensive help text and docstrings -βœ… **User Experience**: Clear, helpful, professional messages -βœ… **Constitutional Compliance**: No stub implementations, all features fully working - -## UI Pattern Usage - -### StatusDisplay Methods Used -- `display.print_command_header(command, title)` - Command header -- `display.print_section(title)` - Section headers -- `display.print_success(message)` - Success messages (green checkmark) -- `display.print_warning(message)` - Warning messages (yellow) -- `display.print_error(message)` - Error messages (red X) -- `display.print_info(message, prefix=emoji)` - Info messages with optional emoji - -### CLIErrorHandler Usage -```python -display = _display -error_handler = CLIErrorHandler(display, verbose=False, debug=False) - -try: - # Command logic - ... -except Exception as e: - error_handler.handle_error(e, "Command name", exit_code=1) -``` - -## Implementation Details - -### BackgroundState Initialization Pattern - -```python -async def start_background_services( - display: StatusDisplay, - config_path: Path | None = None, - project_path: Path | None = None, -) -> None: - """Start background services with proper initialization.""" - from codeweaver import __version__ as version - from codeweaver.common.statistics import get_session_statistics - from codeweaver.server.background.state import BackgroundState - from codeweaver.server.health_service import HealthService - from codeweaver.server.server import _run_background_indexing - - # Load settings - settings = get_settings() - if project_path: - settings.project_path = project_path - elif isinstance(settings.project_path, Unset): - settings.project_path = get_project_path() - - # Get singletons - provider_registry = ProviderRegistry() - statistics = get_session_statistics() - startup_time = time.time() - - # Create BackgroundState with all required parameters - background_state = BackgroundState( - initialized=False, - settings=settings, - statistics=statistics, - project_path=get_project_path() if isinstance(settings.project_path, Unset) else settings.project_path, - config_path=config_path, - provider_registry=provider_registry, - services_registry=ServicesRegistry(), - model_registry=ModelRegistry(), - health_service=None, - failover_manager=None, - telemetry=telemetry_client, - indexer=Indexer.from_settings(), - startup_time=startup_time, - ) - - # Initialize HealthService - background_state.health_service = HealthService( - provider_registry=provider_registry, - statistics=statistics, - indexer=background_state.indexer, - failover_manager=background_state.failover_manager, - startup_time=startup_time, - ) - - # Initialize and start services - await background_state.initialize() - - # Start background indexing - indexing_task = asyncio.create_task( - _run_background_indexing(background_state, settings, display, verbose=False, debug=False) - ) - background_state.background_tasks.add(indexing_task) - - display.print_success("Background services started successfully") - display.print_info(f"Management server: http://127.0.0.1:{settings.server.management_port}", prefix="🌐") - - # Keep services running until interrupted - try: - await background_state.shutdown_event.wait() - except KeyboardInterrupt: - display.print_warning("Shutting down background services...") - finally: - await background_state.shutdown() -``` - -### Health Check Pattern - -```python -async def is_services_running() -> bool: - """Check if background services are running via management server.""" - try: - import httpx - except ImportError: - return False - - settings_map = get_settings_map() - mgmt_host = settings_map.get("server", {}).get("management_host", "127.0.0.1") - mgmt_port = settings_map.get("server", {}).get("management_port", 9329) - - try: - async with httpx.AsyncClient() as client: - response = await client.get(f"http://{mgmt_host}:{mgmt_port}/health", timeout=2.0) - return response.status_code == 200 - except Exception: - return False -``` - -### Signal-Based Shutdown Pattern - -```python -async def stop_background_services() -> None: - """Stop background services gracefully using signal.""" - # Use signal-based shutdown (more secure than HTTP endpoint) - os.kill(os.getpid(), signal.SIGTERM) -``` - -**Rationale**: -- More secure than HTTP shutdown endpoint -- Works with standard signal handlers -- Triggers graceful shutdown via BackgroundState.shutdown_event -- No network exposure for shutdown operations - -## Testing Results - -### Command Registration -```bash -βœ“ cw --help # Shows start and stop commands -βœ“ cw start --help # Shows start command parameters -βœ“ cw stop --help # Shows stop command help -βœ“ cw status # Shows both background services and MCP server status -``` - -### Command Functionality -```bash -βœ“ Imports successful # All commands import without errors -βœ“ Status displays # Shows management server and MCP server status -βœ“ Start command # Full BackgroundState initialization -βœ“ Stop command # Signal-based graceful shutdown -``` - -### Integration Tests -```bash -βœ“ All server tests pass (7/7) -βœ“ Background services initialize correctly -βœ“ Indexing completes successfully -βœ“ Health checks function properly -``` - -### Final Command Structure -``` -codeweaver/cw -β”œβ”€β”€ config -β”œβ”€β”€ search -β”œβ”€β”€ server -β”œβ”€β”€ start βœ“ NEW - Top-level start command -β”œβ”€β”€ stop βœ“ NEW - Top-level stop command -β”œβ”€β”€ index -β”œβ”€β”€ doctor -β”œβ”€β”€ list (ls) -β”œβ”€β”€ init -└── status βœ“ EXTENDED - Shows background services status -``` - -## Code Quality - -**Architecture Compliance**: ⭐⭐⭐⭐⭐ -- Top-level command modules -- Proper use of StatusDisplay -- Proper use of CLIErrorHandler -- Follows existing patterns - -**Implementation Quality**: ⭐⭐⭐⭐⭐ -- Complete BackgroundState initialization -- Proper dependency injection (HealthService) -- Correct use of singletons (get_session_statistics) -- Full async/await patterns -- Type hints throughout - -**User Experience**: ⭐⭐⭐⭐⭐ -- Clear messaging via StatusDisplay -- Helpful status displays -- Professional appearance -- No stub messages or "not implemented" errors - -**Documentation**: ⭐⭐⭐⭐⭐ -- Comprehensive docstrings -- Clear command help text -- Proper usage examples - -## Success Metrics - -βœ… Phase 3 Core Objectives: -- [x] Start command created as top-level module -- [x] Stop command created as top-level module -- [x] Status command extended with management server info -- [x] Commands registered in main CLI -- [x] StatusDisplay pattern used throughout -- [x] CLIErrorHandler pattern used throughout -- [x] All commands tested and working -- [x] Professional UX maintained -- [x] No stub implementations (Constitutional compliance) - -## Key Corrections from Initial Implementation - -**CRITICAL FIXES**: -1. βœ… Changed from `services.py` with subcommands to separate `start.py` and `stop.py` modules -2. βœ… Extended existing `status.py` instead of creating redundant status checking -3. βœ… Used StatusDisplay and CLIErrorHandler patterns throughout -4. βœ… Followed CodeWeaver's top-level command architecture -5. βœ… No stub implementations - all features fully working - -## Next Steps (Future Phases) - -### Phase 1.2: Enable Management Server -- [ ] Enable ManagementServer in BackgroundState.initialize() -- [ ] Start management server during background services startup -- [ ] Test management endpoints accessibility (/health, /metrics, /version) -- [ ] Validate graceful shutdown of management server - -### Integration Testing -- [ ] Test `cw start` β†’ management server starts on port 9329 -- [ ] Test `cw status` β†’ shows "Running" when services active -- [ ] Test `cw stop` β†’ graceful shutdown of all services -- [ ] Test server auto-start behavior (future phase) - -## Conclusion - -Phase 3 successfully implemented **full working** CLI commands for background services management following CodeWeaver's architecture: -- Top-level command modules (`start.py`, `stop.py`) -- Extended existing `status.py` command -- Used StatusDisplay and CLIErrorHandler patterns -- Provides fully functional commands with proper initialization -- Maintains excellent user experience -- Complies with project constitution (no stubs) -- Sets solid foundation for Phase 1.2 - -**Architecture Compliance**: All commands follow CodeWeaver's established patterns and conventions. - -The CLI structure is complete and all commands are fully operational, ready for Phase 1.2 management server integration. diff --git a/claudedocs/refactor-phases-3-6-completion-summary.md b/claudedocs/refactor-phases-3-6-completion-summary.md deleted file mode 100644 index 3ce5fa4d..00000000 --- a/claudedocs/refactor-phases-3-6-completion-summary.md +++ /dev/null @@ -1,368 +0,0 @@ - - -# CodeWeaver Server Refactor - Phases 3-6 Completion Summary - -## Overview -Successfully completed Phases 3-6 of the CodeWeaver server refactor, enabling stdio protocol support for MCP registry compatibility by decoupling background services from the MCP HTTP server. - -**Status**: βœ… **COMPLETE** - All phases tested and validated - ---- - -## Phase 3: Wire Up CLI Commands βœ… - -### Changes Made -**File**: `/home/knitli/codeweaver/src/codeweaver/cli/commands/start.py` - -**What Changed**: Complete rewrite of `start_cw_services()` function to use new lifespan architecture - -**Before** (~90 lines): -- Manual CodeWeaverState creation -- Direct service initialization (health, telemetry, indexing) -- Complex error handling and cleanup - -**After** (~25 lines): -- Delegates to `background_services_lifespan()` context manager -- Clean separation of concerns -- Automatic lifecycle management - -**Key Improvements**: -- Removed manual state initialization -- Removed unused imports (contextlib, time, ModelRegistry, etc.) -- Added proper null check for management_server.server_task -- Maintained all original functionality with cleaner code - ---- - -## Phase 4: Transport Mode Integration Testing βœ… - -### Runtime Configuration Bugs Fixed - -During testing, discovered and fixed **7 critical runtime configuration errors** that weren't caught by type checking: - -#### Bug 1: `transport` Parameter -**Error**: `TypeError: FastMCP.__init__() got an unexpected keyword argument 'transport'` - -**Fix**: `/home/knitli/codeweaver/src/codeweaver/mcp/server.py:287` -```python -mutable_args.pop("transport", None) -``` - -#### Bug 2: `show_banner` Parameter -**Error**: `TypeError: FastMCP.__init__() got an unexpected keyword argument 'show_banner'` - -**Fix**: Removed from FastMCP constructor args (belongs in run_http_async method) - -#### Bug 3: McpMiddleware Import -**Error**: `NameError: name 'McpMiddleware' is not defined` - -**Fix**: `/home/knitli/codeweaver/src/codeweaver/mcp/server.py:28` -```python -from codeweaver.mcp.middleware import McpMiddleware # Moved from TYPE_CHECKING -``` - -#### Bug 4: Middleware Configuration vs Classes -**Error**: `AttributeError: 'str' object has no attribute '__name__'` - -**Root Cause**: Settings contained middleware option names (strings), not class instances - -**Fix**: Always use `default_middleware_for_transport()` to get proper class types - -#### Bug 5: Middleware Instances vs Classes -**Error**: Pydantic validation expecting instances, receiving classes - -**Fix**: `/home/knitli/codeweaver/src/codeweaver/mcp/server.py:329` -```python -return CwMcpHttpState.from_app( - app, **(mutable_args | {"run_args": run_args, "middleware": app.middleware}) -) -``` -Pass `app.middleware` (instances) instead of middleware class list - -#### Bug 6: Host/Port Duplication -**Error**: `TypeError: uvicorn.config.Config() got multiple values for keyword argument 'host'` - -**Root Cause**: Settings had host/port in both top-level run_args AND uvicorn_config - -**Fix**: `/home/knitli/codeweaver/src/codeweaver/mcp/server.py:108-112` -```python -# host, port, and name go in run_args top-level only (FastMCP extracts them) -# Also filter out invalid uvicorn.Config parameters -invalid_params = {"host", "port", "name", "data_header"} -uvicorn_config = {k: v for k, v in uvicorn_config.items() if k not in invalid_params} -``` - -#### Bug 7: Invalid Uvicorn Parameters -**Error**: `TypeError: Config.__init__() got an unexpected keyword argument 'name'` -**Error**: `TypeError: Config.__init__() got an unexpected keyword argument 'data_header'` - -**Fix**: Filter out parameters that uvicorn.Config doesn't accept - -### Additional Fixes - -#### Pydantic Validation Error - log_config Structure -**Error**: 9 validation errors for uvicorn logging configuration - -**Issues**: -- `formatters.default.()` should be `class_name` -- `handlers.null.class` should be `class_name` -- `loggers.*.level` must be numeric (0, 10, 20, 30, 40, 50) not strings - -**Fix**: `/home/knitli/codeweaver/src/codeweaver/mcp/server.py:129-137` -- Simplified to avoid custom log_config -- Just use `log_level: "critical"` and `access_log: False` - -#### Run Args Configuration -**File**: `/home/knitli/codeweaver/src/codeweaver/main.py:176-179` - -**What Changed**: Proper handling of run_args for FastMCP - -**Before** (broken): -```python -await mcp_state.app.run_http_async( - **(mcp_state.run_args | {"show_banner": False, "log_level": "error"}) -) -``` - -**After** (working): -```python -# run_args contains both top-level params and uvicorn_config -# FastMCP.run_http_async() handles host/port specially -# So we just pass run_args directly, which already has everything configured -await mcp_state.app.run_http_async(**mcp_state.run_args) -``` - ---- - -## Phase 5: Cleanup Legacy Code βœ… - -### Verification Results -- βœ… No legacy `build_app()` function found -- βœ… No legacy `start_server()` function found -- βœ… `__all__` exports correct: `("create_http_server", "create_stdio_server")` -- βœ… No unused imports (ruff check passed) -- βœ… No type errors (pyright check passed) -- βœ… No TODO/FIXME comments - ---- - -## Phase 6: Integration Testing βœ… - -### Test Matrix - All Passed - -#### Test 1: HTTP Mode (streamable-http) βœ… -**Command**: `cw server --transport streamable-http` - -**Results**: -- βœ… MCP Server listening on 127.0.0.1:9328 (HTTP 307 redirect) -- βœ… Management Server listening on 127.0.0.1:9329 (HTTP 503/200) -- βœ… Background services initialize (indexing, health checks) -- βœ… Both endpoints respond correctly - -#### Test 2: stdio Mode βœ… -**Command**: `cw server --transport stdio` - -**Results**: -- βœ… Server starts without configuration errors -- βœ… Waits for stdio input (expected behavior) -- βœ… No runtime TypeErrors or validation errors - -#### Test 3: Daemon Mode βœ… -**Command**: `cw start` - -**Results**: -- βœ… Background services start successfully -- βœ… Management Server listening on 127.0.0.1:9329 -- βœ… MCP Server NOT running (correct - daemon mode only) -- βœ… Port 9328 connection timeout (expected) -- βœ… Background indexing initializes - ---- - -## Files Modified - -### Primary Changes -1. `/home/knitli/codeweaver/src/codeweaver/cli/commands/start.py` - - Rewrote `start_cw_services()` to use new lifespan - - Removed ~90 lines of manual initialization - - Added proper error handling - -2. `/home/knitli/codeweaver/src/codeweaver/main.py` - - Fixed shutdown handler (two Ctrl+C pattern) - - Simplified run_http_async() call - - Removed unnecessary parameter passing - -3. `/home/knitli/codeweaver/src/codeweaver/mcp/server.py` - - Fixed 7 runtime configuration bugs - - Added `configure_uvicorn_logging()` function - - Filtered invalid uvicorn parameters - - Fixed middleware handling (classes vs instances) - -4. `/home/knitli/codeweaver/src/codeweaver/mcp/state.py` - - No rebuild_dataclass needed (forward references work with `from __future__ import annotations`) - -### Code Quality -- βœ… All type checks pass (pyright) -- βœ… All linting passes (ruff) -- βœ… No unused imports -- βœ… Clean separation of concerns -- βœ… Proper error handling - ---- - -## Architecture Improvements - -### Before Refactor -``` -HTTP Server Mode: - β”œβ”€ Manual CodeWeaverState creation - β”œβ”€ Hardcoded MCP server lifecycle - β”œβ”€ Background services tied to HTTP mode - └─ stdio mode not possible - -Daemon Mode: - β”œβ”€ Duplicate initialization logic - └─ No code reuse with server mode -``` - -### After Refactor -``` -HTTP Server Mode: - β”œβ”€ create_http_server() β†’ CwMcpHttpState - β”œβ”€ http_lifespan() manages: - β”‚ β”œβ”€ background_services_lifespan() (reusable) - β”‚ β”œβ”€ Management Server (9329) - β”‚ └─ MCP HTTP Server (9328) - └─ Clean shutdown on Ctrl+C - -stdio Mode: - β”œβ”€ create_stdio_server() β†’ FastMCP - β”œβ”€ Background HTTP server for proxy - └─ MCP stdio protocol support - -Daemon Mode: - β”œβ”€ background_services_lifespan() (shared) - β”œβ”€ Management Server only (9329) - └─ No MCP server (correct) -``` - -### Key Benefits -1. **Code Reuse**: background_services_lifespan() shared across modes -2. **Clean Separation**: MCP server independent of background services -3. **stdio Support**: Enables MCP registry compatibility -4. **Maintainability**: Single source of truth for service lifecycle -5. **Testability**: Each component can be tested independently - ---- - -## Validation Checklist - -### Functionality βœ… -- [x] HTTP mode starts both servers (9328, 9329) -- [x] stdio mode initializes correctly -- [x] Daemon mode starts background services only -- [x] Management server responds in all modes -- [x] Background services initialize properly -- [x] Clean shutdown works (Ctrl+C) - -### Code Quality βœ… -- [x] No type errors (pyright) -- [x] No linting errors (ruff) -- [x] No unused imports -- [x] No legacy functions -- [x] Proper __all__ exports -- [x] Clean git status - -### Architecture βœ… -- [x] Lifespan separation achieved -- [x] Code reuse implemented -- [x] stdio protocol supported -- [x] Background services decoupled -- [x] Management server independent - ---- - -## Lessons Learned - -### Runtime vs Type-Time Validation -**Issue**: 7 critical bugs passed type checking but failed at runtime - -**Cause**: -- Pydantic dataclass validation happens at runtime -- TypedDict parameters passed as dicts bypass static type checking -- Settings configuration structure didn't match runtime expectations - -**Solution**: -- Added runtime parameter filtering in `configure_uvicorn_logging()` -- Always validate settings against actual framework APIs -- Use integration tests to catch configuration mismatches - -### Middleware Configuration Complexity -**Issue**: Settings contained middleware names (strings), code expected classes - -**Learning**: Configuration schemas should clearly separate: -- Configuration options (what to enable) -- Implementation classes (how to implement) - -**Solution**: Always use factory functions (`default_middleware_for_transport()`) to map config to classes - -### Pydantic Forward References -**Issue**: Initial attempt to use `rebuild_dataclass()` broke PrivateAttr fields - -**Learning**: With `from __future__ import annotations`, forward references work automatically for TYPE_CHECKING imports - -**Solution**: Removed rebuild attempt, relied on Python's built-in forward reference resolution - ---- - -## Next Steps (Future Work) - -### Recommended -1. Add integration tests for all three modes -2. Document the new architecture in main README -3. Add examples for stdio mode usage -4. Create migration guide for users - -### Optional Enhancements -1. Add health check for MCP server connectivity -2. Implement graceful degradation if background services fail -3. Add metrics for server startup time -4. Create systemd service files for daemon mode - ---- - -## Success Metrics - -| Metric | Before | After | Improvement | -|--------|--------|-------|-------------| -| Lines of code (start.py) | ~120 | ~50 | -58% | -| Code reuse | 0% | 100% | Background services shared | -| Supported protocols | 1 (HTTP) | 2 (HTTP + stdio) | +100% | -| Runtime bugs found | N/A | 7 | All fixed | -| Type errors | 0 | 0 | Maintained | -| Test coverage | Manual | Comprehensive | All modes validated | - ---- - -## Conclusion - -Phases 3-6 successfully completed with all objectives met: - -βœ… **Phase 3**: CLI commands wired to new architecture -βœ… **Phase 4**: All runtime bugs discovered and fixed -βœ… **Phase 5**: Legacy code cleaned up -βœ… **Phase 6**: All modes tested and validated - -The refactored architecture now supports stdio protocol for MCP registry compatibility while maintaining clean separation of concerns and enabling code reuse across deployment modes. - -**Total Session Time**: ~3 hours -**Bugs Fixed**: 7 critical runtime configuration errors -**Code Quality**: All checks passing (pyright, ruff) -**Test Coverage**: 3 modes validated (HTTP, stdio, daemon) - -πŸŽ‰ **Refactor Complete and Production Ready!** diff --git a/claudedocs/refactor-phases-3-6-implementation-guide.md b/claudedocs/refactor-phases-3-6-implementation-guide.md deleted file mode 100644 index 28dcab84..00000000 --- a/claudedocs/refactor-phases-3-6-implementation-guide.md +++ /dev/null @@ -1,613 +0,0 @@ - - -# CodeWeaver Server Refactor: Phases 3-6 Implementation Guide - -## Status: Phases 1-2 Complete βœ… - -**Phase 1 Complete**: Lifespan architecture separated into `background_services_lifespan()` and `http_lifespan()` -**Phase 2 Complete**: Main orchestration implemented with `run()`, `_run_http_server()`, `_run_stdio_server()` - -**Current State**: The core architecture is complete. Backend services are decoupled from MCP server. Main orchestration properly routes between HTTP and stdio modes. - ---- - -## Phase 3: Wire Up CLI Commands - -**Goal**: Update CLI commands to use the new `main.run()` orchestrator - -**Risk**: 🟒 LOW -**Complexity**: LOW -**Time**: 20-30 min - -### Files to Modify - -#### 1. `/home/knitli/codeweaver/src/codeweaver/cli/commands/server.py` - -**Current State**: Already calls `main.run()` - likely needs no changes! - -**Verification**: -```bash -# Check current implementation -grep -A 5 "from codeweaver.main import run" src/codeweaver/cli/commands/server.py -``` - -**If changes needed**: -The `_run_server()` function should simply call `main.run()` with all parameters passed through. Current implementation at line 40 already does this. - -**No action required** - this file is already correctly wired! - ---- - -#### 2. `/home/knitli/codeweaver/src/codeweaver/cli/commands/start.py` - -**Current State**: Has manual `start_cw_services()` implementation that duplicates background service logic - -**Required Changes**: Replace manual service startup with new `background_services_lifespan()` - -**Implementation**: - -```python -# File: src/codeweaver/cli/commands/start.py -# Function: start_cw_services() - -async def start_cw_services( - display: StatusDisplay, - config_path: Path | None = None, - project_path: Path | None = None, - *, - start_mcp_http_server: bool = False, # Currently unused, reserved for future - mcp_host: str | None = None, - mcp_port: PositiveInt | None = None, -) -> None: - """Start background services using the new lifespan architecture.""" - - from codeweaver.config.settings import get_settings - from codeweaver.common.statistics import get_session_statistics - from codeweaver.server.lifespan import background_services_lifespan - from codeweaver.server.management import ManagementServer - - # Load settings - settings = get_settings() - if config_path: - settings.config_file = config_path # type: ignore - if project_path: - settings.project_path = project_path - - statistics = get_session_statistics() - - # Use background_services_lifespan (the new Phase 1 implementation) - async with background_services_lifespan( - settings=settings, - statistics=statistics, - status_display=display, - verbose=False, - debug=False, - ) as background_state: - # Start management server - mgmt_host = getattr(settings, "management_host", "127.0.0.1") - mgmt_port = getattr(settings, "management_port", 9329) - - management_server = ManagementServer(background_state) - await management_server.start(host=mgmt_host, port=mgmt_port) - - display.print_success("Background services started successfully") - display.print_info( - f"Management server: http://{mgmt_host}:{mgmt_port}", prefix="🌐" - ) - - try: - # Keep services running until interrupted - await management_server.server_task - except (KeyboardInterrupt, asyncio.CancelledError): - display.print_warning("Shutting down background services...") - finally: - await management_server.stop() -``` - -**What This Does**: -- Replaces manual CodeWeaverState creation with `background_services_lifespan()` -- Uses the lifespan context manager for proper initialization/cleanup -- Starts management server with background state -- Waits on management server task (keeps running) -- Clean shutdown on Ctrl+C - -**Lines to Replace**: Approximately lines 71-159 in current start.py - -**Testing**: -```bash -# After changes -codeweaver start --verbose - -# Should show: -# - Background services starting -# - Health checks -# - Management server ready -# - Clean shutdown on Ctrl+C -``` - ---- - -### Phase 3 Validation - -**Checklist**: -- [ ] `server.py` verified (likely no changes needed) -- [ ] `start.py` updated to use `background_services_lifespan()` -- [ ] Import test: `python -c "from codeweaver.cli.commands import start, server"` -- [ ] Type check: `ty check src/codeweaver/cli/commands/start.py` -- [ ] Manual test: `codeweaver start` starts successfully -- [ ] Manual test: `codeweaver server` starts successfully - -**Success Criteria**: -- `codeweaver start` runs background services + management server only -- `codeweaver server` runs full HTTP stack -- Both commands shutdown cleanly on Ctrl+C -- No import errors or type errors - ---- - -## Phase 4: Transport Mode Integration Testing - -**Goal**: End-to-end validation that both transport modes work correctly - -**Risk**: 🟑 MEDIUM -**Complexity**: MEDIUM -**Time**: 30-45 min - -### Test Scenarios - -#### Test 1: HTTP Mode Full Stack - -```bash -# Start HTTP server -codeweaver server --transport streamable-http --verbose - -# Expected output: -# - CodeWeaver header with host:port -# - Health checks running -# - Background indexing starting -# - Management server ready at :9329 -# - MCP server ready at :9328 - -# In another terminal, verify endpoints: -curl http://localhost:9328/mcp # MCP endpoint -curl http://localhost:9329/health # Management endpoint - -# Verify both respond correctly -``` - -**Expected Behavior**: -- Server starts without errors -- Both MCP (9328) and Management (9329) servers respond -- Background indexing runs -- Health checks pass (or show degraded state if Qdrant down) -- Clean shutdown on Ctrl+C - -#### Test 2: stdio Mode Proxy - -**Setup**: -```bash -# Terminal 1: Start HTTP backend first -codeweaver server --transport streamable-http - -# Terminal 2: Test stdio proxy -codeweaver server --transport stdio -``` - -**Expected Behavior**: -- stdio server connects to HTTP backend -- stdio server blocks waiting for MCP protocol input -- Ctrl+C shuts down stdio server cleanly -- HTTP backend continues running - -**Note**: Full stdio testing requires an MCP client. For now, verify: -- stdio server starts without errors -- Clean shutdown works -- No error messages about connection to backend - -#### Test 3: Daemon Mode - -```bash -# Start background services only -codeweaver start --verbose - -# Verify management server: -curl http://localhost:9329/health -curl http://localhost:9329/status -curl http://localhost:9329/metrics - -# Verify no MCP server: -curl http://localhost:9328/mcp # Should fail - connection refused -``` - -**Expected Behavior**: -- Management server responds on 9329 -- MCP server NOT running (connection refused on 9328) -- Background indexing runs -- Clean shutdown on Ctrl+C - ---- - -### Phase 4 Validation - -**Checklist**: -- [ ] HTTP mode: Both servers (MCP + Management) respond -- [ ] HTTP mode: Background indexing runs successfully -- [ ] HTTP mode: Clean shutdown on Ctrl+C -- [ ] stdio mode: Server starts and connects to backend -- [ ] stdio mode: Clean shutdown on Ctrl+C -- [ ] Daemon mode: Management server only (no MCP) -- [ ] Daemon mode: Background services run -- [ ] All modes: No error messages or tracebacks - -**Success Criteria**: -- All three modes start successfully -- All three modes shutdown cleanly -- HTTP endpoints respond correctly -- No regressions in existing functionality - ---- - -## Phase 5: Cleanup Legacy Code - -**Goal**: Remove old unused code that's been replaced by new architecture - -**Risk**: 🟒 LOW -**Complexity**: LOW -**Time**: 15-20 min - -### Code to Remove - -#### File: `/home/knitli/codeweaver/src/codeweaver/server/server.py` - -**Remove**: - -1. **Old `start_server()` function** - No longer used, replaced by `main._run_http_server()` - - Search for: `async def start_server(` - - Likely around line 446-499 - - This was the old server startup logic - -2. **Old `build_app()` function** - If it exists, no longer used - - Search for: `def build_app(` - - This was likely used for building the combined app - -**Keep**: -- `CodeWeaverState` dataclass βœ… -- `get_state()` global accessor βœ… -- `_initialize_cw_state()` helper βœ… -- `_cleanup_state()` helper βœ… -- `lifespan()` function βœ… (if still exists, used by tests) - -**Update `__all__` export**: -```python -__all__ = ( - "CodeWeaverState", - "get_state", - # Remove any references to start_server, build_app -) -``` - -**Validation**: -```bash -# Check what's exported -python -c "from codeweaver.server.server import *; print(dir())" - -# Verify no import errors -python -c "from codeweaver.server.server import CodeWeaverState, get_state" - -# Type check -ty check src/codeweaver/server/server.py -``` - ---- - -#### File: `/home/knitli/codeweaver/src/codeweaver/main.py` - -**Already Clean**: Phase 2 implementation replaced all old code. No cleanup needed. - ---- - -### Phase 5 Validation - -**Checklist**: -- [ ] Old `start_server()` removed from server.py (if it exists) -- [ ] Old `build_app()` removed from server.py (if it exists) -- [ ] `__all__` updated to remove old exports -- [ ] No unused imports in server.py -- [ ] Type check passes: `ty check src/codeweaver/server/server.py` -- [ ] All tests still pass: `pytest tests/unit/server/` -- [ ] No import errors in dependent code - -**Success Criteria**: -- No old/unused functions remain -- Cleaner, more maintainable codebase -- All tests pass -- No regressions - ---- - -## Phase 6: Integration Testing - -**Goal**: Comprehensive end-to-end validation of all modes and error scenarios - -**Risk**: πŸ”΄ HIGH (this validates everything) -**Complexity**: HIGH -**Time**: 60-90 min - -### Test Matrix - -| Test Case | Command | Expected Result | Validation Method | -|-----------|---------|-----------------|-------------------| -| **HTTP Server** | `codeweaver server` | MCP:9328, Mgmt:9329 | `curl` both endpoints | -| **HTTP Verbose** | `codeweaver server -v` | Logs visible | Check stdout | -| **stdio Server** | `codeweaver server -t stdio` | Proxy works | Starts without error | -| **Daemon Mode** | `codeweaver start` | Mgmt:9329 only | MCP port refuses connection | -| **Custom Config** | `codeweaver server -c config.toml` | Uses config | Check settings endpoint | -| **Graceful Shutdown** | Ctrl+C during index | Clean exit | No errors, exit code 0 | -| **Force Shutdown** | Double Ctrl+C | Immediate exit | Exit code 1 | -| **Port Conflict** | Start two servers | Error message | Clear error about port in use | - ---- - -### Error Scenario Testing - -#### Test: Missing Dependencies (Qdrant) - -```bash -# Stop Qdrant if running -docker stop qdrant 2>/dev/null || true - -# Start server -codeweaver server --verbose - -# Expected: -# - Server starts successfully -# - Vector store shows "degraded" or "down" in health -# - Helpful message: "To enable semantic search: docker run -p 6333:6333 qdrant/qdrant" -# - Server continues with sparse-only search -# - No crashes or errors -``` - -**Validation**: -```bash -curl http://localhost:9329/health | jq '.services.vector_store.status' -# Should show: "down" or "degraded" -``` - ---- - -#### Test: Port Conflicts - -```bash -# Start first server -codeweaver server & -sleep 5 - -# Try to start second server (should fail) -codeweaver server - -# Expected: -# - Clear error message about port 9328 already in use -# - Suggests checking if server already running -# - Exit code non-zero -``` - ---- - -#### Test: Invalid Configuration - -```bash -# Test with nonexistent config file -codeweaver server -c /nonexistent/config.toml - -# Expected: -# - Clear error message about config file not found -# - Exit code non-zero -# - No traceback (clean error handling) -``` - ---- - -### Performance Validation - -**Metrics to Check**: - -1. **Startup Time**: Server should start in < 5 seconds - ```bash - time codeweaver server - # Measure time to "ready" message - ``` - -2. **Memory Usage**: Idle server should use < 500MB - ```bash - # Start server - codeweaver server & - - # After startup, check memory - ps aux | grep codeweaver | awk '{print $6/1024 " MB"}' - ``` - -3. **Indexing Performance**: Should complete without memory leaks - ```bash - # Start server, let it index, wait 10 minutes - # Check memory hasn't grown excessively - ``` - -4. **Health Check Latency**: Health endpoint should respond in < 100ms - ```bash - time curl http://localhost:9329/health - ``` - ---- - -### Regression Testing - -**Critical Functionality to Verify**: - -1. **Background Indexing**: - - [ ] Indexing starts automatically on server start - - [ ] File watcher detects changes - - [ ] Progress is displayed correctly - - [ ] Summary statistics shown at completion - -2. **Health Monitoring**: - - [ ] All services show correct status - - [ ] Degraded states handled gracefully - - [ ] Health endpoint responds correctly - -3. **Statistics**: - - [ ] Request counting works - - [ ] Metrics endpoint returns valid data - - [ ] Statistics persist across requests - -4. **Telemetry** (if enabled): - - [ ] Session starts correctly - - [ ] Events logged properly - - [ ] Clean shutdown sends final telemetry - ---- - -### Phase 6 Validation - -**Comprehensive Checklist**: - -**Functionality**: -- [ ] All test matrix cases pass -- [ ] Error scenarios handled gracefully -- [ ] Performance metrics acceptable -- [ ] No regressions in existing features - -**Code Quality**: -- [ ] All type checks pass -- [ ] All linters pass -- [ ] No TODO comments for critical functionality -- [ ] Documentation updated - -**Deployment Readiness**: -- [ ] Both transport modes work -- [ ] Daemon mode works independently -- [ ] Clean error messages for common failures -- [ ] Logs are clean and helpful - ---- - -## Final Acceptance Criteria - -**The refactor is complete when**: - -βœ… **Architectural**: -- Background services completely independent of MCP server -- stdio and HTTP transports both functional -- Clean separation of concerns maintained - -βœ… **Functional**: -- `codeweaver server` starts HTTP MCP + background + management -- `codeweaver server -t stdio` starts stdio proxy -- `codeweaver start` starts background services only -- All modes shutdown cleanly - -βœ… **Quality**: -- All type checks pass -- All linters pass -- No regressions in tests -- Code is cleaner than before - -βœ… **Tested**: -- HTTP mode: Both servers respond, indexing works -- stdio mode: Proxy connects and works -- Daemon mode: Management only, no MCP -- Error cases handled gracefully -- Performance acceptable - ---- - -## Quick Reference Commands - -### Testing Commands - -```bash -# Phase 3 validation -codeweaver start --verbose -codeweaver server --verbose - -# Phase 4 validation -codeweaver server --transport streamable-http -codeweaver server --transport stdio -curl http://localhost:9328/mcp -curl http://localhost:9329/health - -# Phase 5 validation -ty check src/codeweaver/server/server.py -pytest tests/unit/server/ - -# Phase 6 validation -# Run all test matrix commands -# Run all error scenario tests -# Check performance metrics -``` - -### Debugging Commands - -```bash -# Check what's running -ps aux | grep codeweaver -lsof -i :9328 -lsof -i :9329 - -# Check logs -tail -f ~/.local/state/codeweaver/logs/codeweaver.log - -# Test endpoints -curl -v http://localhost:9328/mcp -curl -v http://localhost:9329/health | jq '.' -curl -v http://localhost:9329/status | jq '.' -``` - ---- - -## Rollback Strategy - -If something breaks: - -```bash -# Phase 3 issues -git reset --hard - -# Phase 4 issues -# No code changes, just testing - -# Phase 5 issues -git reset --hard - -# Phase 6 issues -# No code changes, just testing -``` - ---- - -## Notes for Future Implementer - -**What's Done**: -- Phase 1: Lifespan architecture separated and working -- Phase 2: Main orchestration complete and working -- All type checks pass -- Architecture is sound - -**What's Left**: -- Phase 3: Mostly updating start.py (~30 lines) -- Phase 4: Just testing, no code changes -- Phase 5: Removing old code (~50 lines) -- Phase 6: Comprehensive testing, no code changes - -**Estimated Time**: 2-3 hours total for phases 3-6 - -**Key Files**: -- `src/codeweaver/cli/commands/start.py` - Main work in Phase 3 -- `src/codeweaver/server/server.py` - Cleanup in Phase 5 -- Everything else is testing and validation - -**The Hard Part is Done**: The core architecture (phases 1-2) is complete and working. What remains is wiring and testing. diff --git a/claudedocs/research_codeweaver_competitive_analysis_2025-11-17.md b/claudedocs/research_codeweaver_competitive_analysis_2025-11-17.md deleted file mode 100644 index c04d72f4..00000000 --- a/claudedocs/research_codeweaver_competitive_analysis_2025-11-17.md +++ /dev/null @@ -1,492 +0,0 @@ - - -# CodeWeaver Competitive Analysis: Codebase Search & Discovery - -**Research Date**: November 17, 2025 -**Focus**: Semantic code search, codebase discovery, and indexing alternatives to CodeWeaver -**Scope**: Emphasis on search/discovery capabilities rather than MCP server architecture - -## Executive Summary - -CodeWeaver occupies a unique position in the code search landscape, combining: -- **Portability**: Not tied to specific IDEs or language servers (unlike Cursor, Copilot, LSP-based tools) -- **Model flexibility**: 20+ embedding providers, 50+ models vs competitors' 1-3 options -- **Speed**: 3.2s indexing for 77k LOC codebase (excluding embedding generation) -- **Hybrid search**: Dense + sparse embeddings by default (rare in current market) -- **Language support**: 26 languages with semantic AST parsing, 170+ with intelligent delimiter chunking -- **Deployment flexibility**: Local, cloud, airgapped environments with intelligent failover - -The competitive landscape splits into three categories: -1. **IDE-integrated tools** (Cursor, GitHub Copilot, VSCode) -2. **Dedicated code search platforms** (Sourcegraph, Bloop, Continue.dev, Aider) -3. **Traditional search tools** (ripgrep, OpenGrok, LSP-based navigation) - ---- - -## Competitive Landscape Overview - -### Category 1: IDE-Integrated Semantic Search - -#### **Cursor IDE** -- **Embedding approach**: Remote processing via OpenAI or custom model -- **Architecture**: Client chunks locally β†’ sends to Cursor servers β†’ remote embedding β†’ remote vector DB -- **Search method**: RAG with @Codebase command -- **Recent improvements** (Nov 2024): New embedding model, improved accuracy/speed -- **Language support**: Not explicitly documented; relies on general-purpose embeddings -- **Privacy concerns**: Code chunks sent to external servers by default -- **Local option**: Experimental ChromaDB-based local indexing via MCP server -- **Strengths**: Deep IDE integration, GitHub PR/issue semantic search -- **Weaknesses**: Requires internet, privacy concerns, tied to Cursor IDE - -#### **GitHub Copilot Workspace** (2024 release) -- **Embedding approach**: Remote code search indexes (GitHub/Azure DevOps) -- **Architecture**: Remote indexing service, not local -- **Search method**: #codebase context variable in chat -- **Enterprise**: Can index organization's entire codebase -- **Language support**: Repository-wide, language-agnostic -- **Strengths**: Full GitHub integration, understands repo structure/issues/PRs -- **Weaknesses**: Cloud-only, requires GitHub/Azure DevOps, not portable - -#### **Sourcegraph Cody** -- **Evolution**: Originally used OpenAI text-embedding-ada-002 for embeddings -- **Current approach**: **Replaced embeddings with Sourcegraph Search** for Enterprise -- **Architecture**: No longer requires third-party embedding processors -- **Search method**: Sourcegraph's native code search (keyword + structural) -- **Local embeddings**: Only in VS Code for personal projects -- **Strengths**: Scales to massive codebases, no external LLM calls for search -- **Weaknesses**: Sourcegraph Enterprise required for best features, not purely semantic - ---- - -### Category 2: Dedicated Code Search Platforms - -#### **Bloop** -- **Architecture**: Rust-based, local-first, privacy-focused -- **Embedding approach**: On-device embeddings (model not specified in sources) -- **Vector store**: Qdrant -- **Search index**: Tantivy (Rust search engine similar to Lucene) -- **Search types**: Semantic + regex + precise navigation -- **Deployment**: Lightweight desktop app (Tauri framework) -- **Strengths**: Fast, local processing, multi-platform, combines search types -- **Weaknesses**: Single embedding model, limited customization, desktop app only - -#### **Continue.dev** -- **Default embedding**: Transformers.js with all-MiniLM-L6-v2 (384 dimensions, local in IDE) -- **Supported providers**: Ollama (nomic-embed-text recommended), Voyage AI (voyage-code-2), OpenAI, HuggingFace TEI -- **Chunking**: Tree-sitter AST-based, pulls top-level functions/classes -- **Retrieval**: Combined embeddings + keyword search -- **Reranking**: Cohere, Voyage, LLM-based, HuggingFace TEI, free-trial -- **Configuration**: nRetrieve (default 25) β†’ rerank β†’ nFinal (default 5) -- **Index storage**: ~/.continue/index/index.sqlite -- **Strengths**: Multiple embedding providers, reranking support, IDE-integrated -- **Weaknesses**: Tied to VS Code/JetBrains, configuration complexity - -#### **Aider** -- **Approach**: Repository map rather than embeddings -- **Technology**: Tree-sitter for AST parsing + ctags for symbol extraction -- **Method**: Graph ranking algorithm on dependency graph -- **Token optimization**: Default 1k tokens for repo map (--map-tokens) -- **Content**: Files + key symbols + critical code lines + call signatures -- **Use case**: Terminal-based AI pair programming, not interactive search -- **Strengths**: Lightweight, effective for LLM context without embeddings -- **Weaknesses**: Not a search tool, limited to repository mapping for context - ---- - -### Category 3: Traditional Search & Navigation Tools - -#### **LSP (Language Server Protocol)** -- **Approach**: Static analysis via AST parsing, symbol tables, cross-references -- **Capabilities**: Go-to-definition, find-references, rename-across-files -- **LSIF**: Language Server Index Format for pre-built indexes (serialized LSP data) -- **Strengths**: Precise structural understanding, scope-aware, inheritance/overloading support -- **Weaknesses**: Structural only (no semantic similarity), requires language server per language -- **Complementarity**: Combines well with semantic search (LSP=structural, embeddings=semantic) - -#### **ripgrep / The Silver Searcher (ag)** -- **Type**: Fast text-based code search -- **Performance**: ripgrep ~10x faster than grep, often faster than ag -- **Technology**: ripgrep uses Rust regex (finite automata), ag uses PCRE (backtracking) -- **Adoption**: Integrated into VS Code, widely used in terminal workflows -- **Strengths**: Extremely fast, respects .gitignore, no indexing needed -- **Weaknesses**: Keyword-only, no semantic understanding, no embeddings - -#### **OpenGrok** -- **Type**: Enterprise code search and cross-reference engine -- **Technology**: Java-based, full-text + definition + symbol + path + revision search -- **VCS support**: Git, SVN, Mercurial, Perforce, ClearCase, etc. -- **Features**: Syntax highlighting, incremental updates, Google-like syntax -- **Strengths**: Mature, handles large repos, version control integration -- **Weaknesses**: No semantic embeddings, no AI integration, requires Java infrastructure - ---- - -## Technical Feature Comparison - -### Embedding Models & Providers - -| Solution | Embedding Providers | Models Supported | Code-Specific Models | -|----------|-------------------|------------------|---------------------| -| **CodeWeaver** | **20+ providers** | **50+ models** | βœ… Voyage Code, Mistral Codestral, specialized models | -| Cursor | OpenAI, custom | 1-2 models | ❌ General-purpose | -| Copilot Workspace | Microsoft/OpenAI | 1 model | ❌ General-purpose | -| Sourcegraph Cody | None (deprecated) | N/A | ❌ Uses keyword search instead | -| Bloop | Local model | 1 model | ⚠️ Unspecified | -| Continue.dev | 4-5 providers | 5-10 models | βœ… Voyage Code support | - -**CodeWeaver advantage**: Only solution with 20+ providers and 50+ models, including specialized code embeddings (Voyage Code-2, Mistral Codestral). - -### Hybrid Search (Dense + Sparse Embeddings) - -| Solution | Dense | Sparse | Hybrid | Method | -|----------|-------|--------|--------|--------| -| **CodeWeaver** | βœ… | βœ… | **βœ…** | Dense embeddings + BM25/SPLADE | -| Cursor | βœ… | ❌ | ❌ | Dense only | -| Copilot Workspace | βœ… | ❌ | ❌ | Dense only | -| Continue.dev | βœ… | βœ… | βœ… | Dense + keyword search | -| Bloop | βœ… | ❌ | ⚠️ | Dense + regex (not true sparse embeddings) | - -**Hybrid search context**: -- **BM25**: Traditional keyword matching with TF-IDF enhancement -- **SPLADE**: Neural sparse embeddings with term expansion via BERT -- **Best practice**: Industry consensus favors hybrid (dense for semantics, sparse for exact matches) -- **Performance**: Voyage Code-2 + sparse embeddings = 14.52% improvement vs OpenAI dense-only - -**CodeWeaver advantage**: One of only two solutions with true hybrid dense + sparse embeddings. - -### Language Support - -| Solution | AST/Semantic Parsing | Fallback Chunking | Total Languages | -|----------|---------------------|-------------------|----------------| -| **CodeWeaver** | **26 languages** (tree-sitter) | **170+ languages** (intelligent delimiters) | **170+** | -| Cursor | ⚠️ Unspecified | ⚠️ Unspecified | ~50-100 (estimated) | -| Copilot Workspace | Language-agnostic | N/A | All (text-based) | -| Continue.dev | Tree-sitter | Top-level only | ~165 (tree-sitter-language-pack) | -| Aider | Tree-sitter | ctags fallback | ~165+ (tree-sitter + ctags) | -| Bloop | ⚠️ Unspecified | ⚠️ Unspecified | Unknown | - -**Tree-sitter language support context**: -- Modern tree-sitter packages support **165+ languages** (as of 2024) -- Covers all major programming languages + many DSLs - -**CodeWeaver advantages**: -1. **Dual-mode chunking**: Semantic AST for 26 languages, intelligent delimiter-based for 170+ -2. **First-class fallback**: Delimiter chunker uses language family patterns (not just line breaks) -3. **Cross-language normalization**: Normalized AST node types for better multi-language search - -### Indexing Speed - -| Solution | Benchmark | Notes | -|----------|-----------|-------| -| **CodeWeaver** | **3.2s for 77k LOC** | Excludes embedding (provider-dependent) | -| | | Remote: ~5s total; Local: ~20s total | -| Visual Studio C++ | 2.5-6x faster than VS 2019 | Large projects (UnrealEngine 5: 1 min, Chromium: 5 min) | -| Meilisearch | 7x faster than Elasticsearch | General search indexing | -| Elasticsearch | Baseline | Industry standard | -| Bloop | "Fast" (unspecified) | Rust-based, Tantivy + Qdrant | - -**CodeWeaver advantage**: Sub-5s total indexing for mid-size codebases with remote providers. - -### Real-Time Updates & File Watching - -| Solution | File Watching | Incremental Updates | Deduplication | -|----------|---------------|---------------------|---------------| -| **CodeWeaver** | **βœ…** | **βœ… Instant** | **βœ… Move detection** | -| Cursor | ⚠️ Likely | βœ… | ❌ | -| Copilot Workspace | ❌ | ⚠️ Server-side | ❌ | -| Continue.dev | βœ… | βœ… | ❌ | -| Aider | ❌ | ❌ | N/A | -| OpenGrok | ⚠️ Manual/scheduled | βœ… | ❌ | - -**CodeWeaver advantages**: -1. **Instant incremental updates** on file changes (not just periodic reindexing) -2. **Deduplication**: Detects file moves without re-embedding (updates references only) -3. **Live tracking**: Maintains real-time index accuracy - -### Deployment Models - -| Solution | Local | Cloud | Airgapped | Portable | -|----------|-------|-------|-----------|----------| -| **CodeWeaver** | **βœ…** | **βœ…** | **βœ…** | **βœ…** | -| Cursor | ⚠️ Experimental | βœ… Default | ❌ | ❌ (IDE-only) | -| Copilot Workspace | ❌ | βœ… Only | ❌ | ❌ | -| Sourcegraph Cody | βœ… Enterprise | βœ… | βœ… Enterprise | ⚠️ IDE-integrated | -| Bloop | βœ… | ❌ | βœ… | ⚠️ Desktop app | -| Continue.dev | βœ… | ⚠️ Hybrid | βœ… | ❌ (IDE-only) | -| Aider | βœ… | ❌ | βœ… | βœ… (CLI) | - -**Airgapped deployment context** (2024): -- **Enterprise demand**: High for defense, finance, healthcare, government -- **Leading solutions**: Tabnine Enterprise (fully airgapped), Sourcegraph Cody Enterprise -- **Open-source**: Continue.dev supports local models via Ollama -- **Trend**: Increasing demand for on-premises AI tools without external API dependencies - -**CodeWeaver advantages**: -1. **Full offline capability**: With local embedding providers (sentence-transformers, FastEmbed) -2. **Intelligent failover**: In-memory vector store backup when primary DB unavailable -3. **No IDE lock-in**: MCP server architecture works with any MCP-compatible client -4. **Portable**: Not tied to specific editor, language server, or cloud service - ---- - -## Unique CodeWeaver Differentiators - -### 1. **Intelligent Backup & Failover** -**Feature**: Low-resource local embedding backup in JSON format - -**Behavior**: -- Primary vector store (Qdrant) failure β†’ automatic switch to in-memory store -- Maintains live file watching and incremental updates during failover -- Seamless handover when primary store recovers -- Ensures **zero functionality loss**, only slightly degraded result quality - -**Competitive gap**: No competitors documented with this level of failover sophistication. - ---- - -### 2. **Cross-Language AST Normalization** -**Feature**: Normalized node types across 26 tree-sitter languages - -**Benefit**: Improves cross-language semantic search (e.g., "find all factory patterns" works across Python, Java, TypeScript) - -**Competitive gap**: Continue.dev and Aider use tree-sitter but don't normalize node types across languages. - ---- - -### 3. **Provider Ecosystem Flexibility** -**CodeWeaver**: -- 20 embedding providers (Voyage, OpenAI, Cohere, Mistral, HuggingFace, Azure, AWS Bedrock, Google, etc.) -- 50+ configured models -- 5 reranking providers, 20+ reranking models -- Extensible: New providers need only implement a few methods - -**Closest competitor** (Continue.dev): -- 4-5 embedding providers -- 5-10 models -- Reranking support but fewer options - -**Competitive gap**: **4-10x more embedding options** than any competitor. - ---- - -### 4. **MCP Protocol Integration** -**Context**: MCP (Model Context Protocol) launched by Anthropic in November 2024 - -**Adoption**: -- Integrated in: Visual Studio (GA), Cursor, Claude Code, Replit, Codeium, Sourcegraph -- Vercel's Grep code search added MCP server for AI agent integration -- Growing ecosystem of MCP servers for databases, deployment, code search - -**CodeWeaver positioning**: -- Built as MCP server from the ground up -- Enables integration with any MCP-compatible AI agent/IDE -- Positions CodeWeaver for the emerging multi-agent development ecosystem - -**Competitive gap**: Most competitors built before MCP; retrofitting vs. native design. - ---- - -### 5. **First-Class Delimiter Chunking (170 languages)** -**Feature**: Intelligent delimiter-based chunking for non-AST languages - -**Method**: -- Uses language family patterns (e.g., "what a function looks like in this family") -- Divides on meaningful syntactic boundaries, not arbitrary line counts -- Includes contextual metadata about chunk type - -**Contrast**: -- Most tools: AST chunking with simple line-based fallback -- CodeWeaver: **Semantic-like chunking for 170+ languages** even without tree-sitter support - -**Competitive gap**: Only solution with this level of fallback chunking sophistication. - ---- - -## Market Positioning Analysis - -### CodeWeaver's Market Position - -``` - Portability (IDE-agnostic) - β–² - β”‚ - Aider β”‚ CodeWeaver ⭐ - (CLI) β”‚ (MCP Server) - β”‚ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ β”‚ - Bloop β”‚ β”‚ β”‚ Continue.dev - (Desktop)β”‚ β”‚ β”‚ (VS Code) - β”‚ β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - Cursor β”‚ Copilot Workspace - (Cloud) β”‚ (Cloud) - β”‚ - β–Ό - Customization (Embedding Models) -``` - -**Quadrant positioning**: -- **Top-right** (CodeWeaver): High portability + high customization -- **Bottom-right** (Continue.dev): Medium portability (IDE-integrated) + medium customization -- **Top-left** (Aider): High portability (CLI) + low customization (repo map, no embeddings) -- **Bottom-left** (Cursor, Copilot): Low portability (cloud/IDE-locked) + low customization (1-2 models) - ---- - -### Target Use Cases by Solution - -| Use Case | Best Solution | Why | -|----------|--------------|-----| -| **Individual developer, IDE-integrated** | Continue.dev, Cursor | Deep IDE integration, ease of use | -| **Enterprise, airgapped environment** | **CodeWeaver**, Tabnine, Sourcegraph | Local deployment, no external APIs | -| **Multi-IDE teams** | **CodeWeaver** | IDE-agnostic MCP server | -| **Large-scale repos (>1M LOC)** | Sourcegraph Cody | Deprecated embeddings, uses scalable keyword search | -| **Terminal-based workflows** | Aider, ripgrep | CLI-first, lightweight | -| **GitHub-centric teams** | Copilot Workspace | Native GitHub integration | -| **Custom embedding models** | **CodeWeaver**, Continue.dev | Provider flexibility | -| **Offline/local-first** | **CodeWeaver**, Bloop, Aider | No cloud dependencies | - ---- - -## Competitive Gaps & Opportunities - -### CodeWeaver Leads In: -1. βœ… **Embedding provider diversity** (20+ vs competitors' 1-5) -2. βœ… **Hybrid search** (dense + sparse by default) -3. βœ… **Language support** (170+ with semantic-like chunking) -4. βœ… **Deployment flexibility** (local/cloud/airgapped with failover) -5. βœ… **Portability** (MCP server, not IDE-locked) -6. βœ… **Cross-language normalization** (AST node types) -7. βœ… **Intelligent failover** (backup vector store with live tracking) - -### Areas to Monitor: -1. ⚠️ **IDE integration depth**: Cursor/Copilot have deeper native integration -2. ⚠️ **User experience**: Desktop apps (Bloop) may feel more polished than MCP servers -3. ⚠️ **Repository context breadth**: Copilot Workspace understands issues/PRs/comments -4. ⚠️ **Reranking sophistication**: Continue.dev has multiple reranking options (CodeWeaver has 5 providers, 20+ models, but integration depth unclear from research) - -### Strategic Opportunities: -1. **MCP ecosystem growth**: Position as the premium MCP code search server -2. **Enterprise airgapped**: Strong differentiator as more companies avoid cloud AI -3. **Multi-language projects**: Cross-language normalization unique in market -4. **Embedding model innovation**: As new code-specific models emerge (Mistral Codestral, etc.), CodeWeaver can adopt faster than competitors due to provider architecture -5. **Hybrid search education**: Few developers understand dense vs sparse embeddings; thought leadership opportunity - ---- - -## Competitive Threats Assessment - -### **High Threat**: Cursor IDE -- **Risk**: Gaining massive developer adoption, improved embedding model (Nov 2024) -- **Mitigation**: CodeWeaver's MCP architecture works *with* Cursor (not against it); position as "Cursor-compatible advanced search" -- **Timeframe**: Ongoing - -### **Medium Threat**: GitHub Copilot Workspace -- **Risk**: GitHub's distribution reach, native integration -- **Mitigation**: Copilot is cloud-only; CodeWeaver's airgapped/local deployment addresses different market -- **Timeframe**: 2024-2025 - -### **Medium Threat**: Sourcegraph Cody -- **Risk**: Enterprise-proven, scalable keyword search replacing embeddings -- **Mitigation**: Different philosophy (embeddings vs keyword search); hybrid approach may be superior -- **Timeframe**: Ongoing - -### **Low Threat**: Continue.dev -- **Risk**: Similar embedding provider strategy, growing adoption -- **Mitigation**: CodeWeaver has 4x more providers/models, MCP portability -- **Timeframe**: 2025 - -### **Low Threat**: Bloop -- **Risk**: Privacy-focused, local-first, fast -- **Mitigation**: Limited customization, single embedding model, desktop app (less portable) -- **Timeframe**: Stable/niche - ---- - -## Recommendations for CodeWeaver - -### **Immediate Priorities** (Alpha release): -1. βœ… **Feature parity documentation**: Ensure users understand hybrid search, provider flexibility, failover -2. βœ… **MCP integration guides**: Show how to use CodeWeaver with Cursor, Claude Code, VS Code -3. βœ… **Benchmark publication**: 3.2s indexing speed vs competitors (with methodology) - -### **Short-term** (Post-Alpha): -1. **Reranking showcase**: Highlight 5 providers, 20+ models vs Continue.dev's reranking -2. **Cross-language search examples**: Demonstrate AST normalization benefits -3. **Airgapped deployment guides**: Target enterprise compliance requirements -4. **Embedding model benchmarks**: Compare Voyage Code-2, Mistral Codestral, OpenAI on real codebases - -### **Long-term** (Ecosystem): -1. **MCP marketplace presence**: List on emerging MCP server directories -2. **Integration partnerships**: Collaborate with Cursor, Codeium, Replit on CodeWeaver integration -3. **Thought leadership**: Hybrid search, code-specific embeddings, airgapped AI -4. **Community embedding models**: Support community-contributed providers (e.g., new HuggingFace models) - ---- - -## Conclusion - -CodeWeaver occupies a **unique high-value position** in the code search market: - -**Primary differentiators**: -1. Most flexible embedding provider ecosystem (20+ providers, 50+ models) -2. True hybrid search (dense + sparse) as default -3. Broadest language support with semantic-like chunking (170+ languages) -4. Only solution with intelligent failover and live backup vector store -5. MCP-native architecture (portable, not IDE-locked) -6. Full offline/airgapped capability with local providers - -**Competitive moat**: -- **Provider flexibility** creates switching costs once users customize embeddings -- **Hybrid search** and **cross-language normalization** address limitations competitors don't recognize yet -- **MCP protocol** positions CodeWeaver for emerging multi-agent development ecosystem -- **Airgapped deployment** addresses enterprise segment underserved by cloud-first competitors - -**Market opportunity**: -- **IDE-integrated tools** (Cursor, Copilot) serve mainstream developers but lack customization -- **Enterprise platforms** (Sourcegraph) scale but abandoned embeddings -- **CodeWeaver** bridges the gap: **Enterprise-grade flexibility + Developer-friendly portability** - -**Success metrics to track**: -1. MCP ecosystem adoption rate (are more IDEs/agents supporting MCP?) -2. Enterprise airgapped AI tool demand (regulatory/compliance drivers?) -3. New code-specific embedding models (can CodeWeaver adopt faster than competitors?) -4. Hybrid search awareness (is the market learning dense+sparse is superior?) - -CodeWeaver is well-positioned to become the **premium semantic code search solution for teams requiring customization, portability, and offline capability**. - ---- - -## Sources & References - -### Primary Research Sources: -1. Cursor IDE documentation and blog posts (cursor.com/blog) -2. GitHub Copilot Workspace announcement (github.blog/2024-04-29) -3. Sourcegraph Cody documentation (docs.sourcegraph.com/cody) -4. Continue.dev documentation (docs.continue.dev) -5. Bloop case study (qdrant.tech/blog/case-study-bloop) -6. Aider repository map documentation (aider.chat/docs/repomap.html) -7. Voyage AI embedding benchmarks (blog.voyageai.com) -8. MCP protocol specification (modelcontextprotocol.io) -9. Various industry benchmarks and comparisons (2024-2025) - -### Key Industry Trends Observed: -- MCP protocol adoption accelerating (Nov 2024 launch β†’ GA in VS Studio) -- Airgapped AI demand increasing (enterprise compliance, security) -- Hybrid search recognition growing (industry best practice) -- Code-specific embedding models emerging (Voyage Code-2, Mistral Codestral) -- Tree-sitter adoption expanding (165+ language support as of 2024) - ---- - -**Research Confidence**: High for feature comparisons, Medium for market sizing/adoption rates (limited public data) - -**Last Updated**: November 17, 2025 diff --git a/docs/WHY.md b/docs/WHY.md index 90249182..ffaaf8f2 100644 --- a/docs/WHY.md +++ b/docs/WHY.md @@ -30,7 +30,7 @@ There are some better implementations out there, but most are integrated into MC **Want to:** - Use your preferred IDE (VIM, Emacs, VS Code, whatever)? -- Switch between different AI agent, embedding (sparse/dense and reranking) providers? +- Switch between different AI agent, embedding (sparse/dense and reranking) providers? - Deploy to your infrastructure? - Customize how context is indexed and retrieved? - Work offline or in airgapped environments? @@ -47,19 +47,21 @@ CodeWeaver's overall goal is to stop that cycle -- give agents exactly what they - Agents read entire files when they need a single function - They re-read the same files across multiple tool calls - They carry massive context windows that drive up token costs -- Even well-meaning MCP tools contribute: several popular MCP servers have **15,000+ tokens in prompt overhead** -- all the prompts they supply **every single message** to tell an agent about their available tools and how to use them +- Even well-meaning MCP tools contribute: several popular MCP servers have **16,000+ tokens in prompt overhead** -- all the prompts they supply **every single message** to tell an agent about their available tools and how to use them [^1] -CodeWeaver takes a different approach: +**CodeWeaver is following a different path**: - Returns only the relevant code fragments - Indexes once, serves efficiently - Minimal MCP protocol overhead (less than 1,000 tokens) - Smart caching and reuse +[^1]: At current API costs ($3/million tokens) for Claude Sonnet 4.5, that's over $4 for a 100 turn session, which is pretty common when you have a large number of MCP tools. + ## 4. Agent-First Tools Most MCP tools are literally just human tools and APIs with an MCP interface. AI agents are purpose-trained to *generate language*, and you've handed them a complex, many-faceted tool that can be used in many different ways, and said "figure it out." -The harder, and better course, is to give AI agents tools *built for them*. Tools that allow them to do what they do best (generate code), and minimize their need to do things they aren't great at. +The harder, and better course, is to give AI agents tools *built for them*. Tools that allow them to do what they do best (generate code), and minimize their need to do things they aren't great at, like make decisions about what tool to use. **Example:** - **Human tools**: Choose from 30 different search options, configure parameters, combine tools, parse results @@ -77,8 +79,7 @@ I don't want to read a novella to figure out how to use a coding tool. But we as ## The Result Without CodeWeaver -Models get random snippets. The indexing falls behind. Offline mode is a coin toss. -Context is shallow, inconsistent, and fragile. +You take your chances that the agent will get the context it needs to success at your task. I think we can change that. ## CodeWeaver's Path @@ -88,8 +89,8 @@ Context is shallow, inconsistent, and fragile. * **Resilient design:** maintains a separate lightweight hybrid vector index that automatically kicks in when your primary providers fail. Your fallback is better than most tools' primary mode. * **Your infrastructure:** deploy however you want, wherever you want -It's not trying to give AI all the tools. -It gives AI one *great* tool. +It's not trying to give AI *all the tools*. +**CodeWeaver gives AI one *great* tool.** --- @@ -97,10 +98,12 @@ It gives AI one *great* tool. CodeWeaver bets that **context quality matters more than context quantity**. -Instead of giving agents every possible tool and letting them figure it out, we give them: +Instead of giving agents every possible tool and letting them figure it out, I aim to give them: - Deep structural understanding of your codebase - Semantic relationships between components - Intent-aware search that understands what they're trying to accomplish - Reliable fallback that works even when everything else fails -This makes agents more effective with less effort, lower cost, and better results. +This makes agents more effective with less effort, lower cost, and better results.[^1] + +[^1]: At least, that's the plan. diff --git a/claudedocs/codeweaver_feature_comparison_chart.md b/docs/comparison.md similarity index 56% rename from claudedocs/codeweaver_feature_comparison_chart.md rename to docs/comparison.md index d1963378..cefb5b7b 100644 --- a/claudedocs/codeweaver_feature_comparison_chart.md +++ b/docs/comparison.md @@ -7,29 +7,27 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # CodeWeaver Feature Comparison Chart -**Last Updated**: November 17, 2025 +**Last Updated**: December 9, 2025 ## Quick Reference Matrix -| Feature | CodeWeaver | Cursor | Copilot Workspace | Sourcegraph Cody | Continue.dev | Bloop | Aider | -|---------|-----------|--------|-------------------|------------------|--------------|-------|-------| -| **Embedding Providers** | **20+** | 1-2 | 1 | 0 (deprecated) | 4-5 | 1 | 0 (no embeddings) | -| **Embedding Models** | **50+** | 1-2 | 1 | 0 | 5-10 | 1 | 0 | -| **Code-Specific Models** | βœ… Yes | ❌ No | ❌ No | N/A | βœ… Yes | ⚠️ Unclear | N/A | -| **Hybrid Search** | **βœ… Dense+Sparse** | ❌ Dense only | ❌ Dense only | ❌ Keyword only | βœ… Dense+Keyword | ⚠️ Dense+Regex | N/A | -| **AST Semantic Parsing** | βœ… 26 langs | ⚠️ Unclear | ❌ No | ⚠️ Unclear | βœ… Tree-sitter | ⚠️ Unclear | βœ… Tree-sitter | -| **Fallback Chunking** | **βœ… 170+ langs** | ⚠️ Unclear | βœ… All | ⚠️ Unclear | ⚠️ Basic | ⚠️ Unclear | βœ… ctags | -| **Total Language Support** | **170+** | ~50-100 | All (text) | All | ~165 | Unknown | ~165+ | -| **Indexing Speed** | **3.2s (77k LOC)** | ⚠️ Unclear | Server-side | ⚠️ Unclear | ⚠️ Unclear | "Fast" | N/A | -| **Live File Watching** | **βœ… Instant** | βœ… Likely | ❌ No | ⚠️ Unclear | βœ… Yes | ⚠️ Unclear | ❌ No | -| **Deduplication** | **βœ… Move detect** | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | N/A | -| **Local Deployment** | **βœ… Full** | ⚠️ Experimental | ❌ No | βœ… Enterprise | βœ… Yes | βœ… Yes | βœ… Yes | -| **Cloud Deployment** | βœ… Yes | βœ… Default | βœ… Only | βœ… Yes | ⚠️ Hybrid | ❌ No | ❌ No | -| **Airgapped Support** | **βœ… Full** | ❌ No | ❌ No | βœ… Enterprise | βœ… Yes | βœ… Yes | βœ… Yes | -| **Portable (IDE-agnostic)** | **βœ… MCP** | ❌ Cursor only | ❌ Cloud only | ⚠️ IDE-tied | ❌ IDE-tied | ⚠️ Desktop app | βœ… CLI | -| **Intelligent Failover** | **βœ… Yes** | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | N/A | -| **Reranking Support** | βœ… 5 providers, 20+ models | ❌ No | ❌ No | ❌ No | βœ… Multiple | ❌ No | N/A | -| **Cross-Language Normalization** | **βœ… Yes** | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | +| Feature | CodeWeaver | Serena | Cursor | Copilot Workspace | Sourcegraph Cody | Continue.dev | Bloop | Aider | +|---------|-----------|--------|--------|-------------------|------------------|--------------|-------|-------| +| **Approach** | Semantic search | Symbol lookup (LSP) | Semantic | Semantic | Keyword | Semantic | Semantic | Repo maps | +| **Tool Count** | **1** | **20+** | N/A | N/A | N/A | N/A | N/A | N/A | +| **Prompt Overhead** | **~500 tokens** | **~16,000 tokens** | N/A | N/A | N/A | N/A | N/A | N/A | +| **Search Speed** | Moderate (embeddings) | **Very fast (LSP)** | Moderate | Server-side | Fast | Moderate | Fast | On-demand | +| **Embedding Providers** | **17** | 0 (no embeddings) | 1-2 | 1 | 0 (deprecated) | 4-5 | 1 | 0 | +| **Language Support** | **166+** | ~30 (LSP required) | ~50-100 | All (text) | All | ~165 | Unknown | ~165+ | +| **Requires Language Server** | ❌ No | βœ… Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | +| **Symbol Precision** | ⚠️ Semantic | **βœ… Exact symbols** | ⚠️ Semantic | ⚠️ Semantic | ⚠️ Keyword | ⚠️ Semantic | ⚠️ Semantic | βœ… Exact | +| **Concept Search** | **βœ… Yes** | ❌ Symbols only | βœ… Yes | βœ… Yes | ⚠️ Limited | βœ… Yes | βœ… Yes | ❌ No | +| **Editing Capabilities** | ❌ No | **βœ… Yes (9 tools)** | βœ… Yes | βœ… Yes | βœ… Yes | βœ… Yes | ❌ No | βœ… Yes | + +**Notes**: +- **Serena tool count**: Varies by Serena's 'context' (20+ in claude-code, up to 35 total available) +- **Serena prompt overhead**: Measured with 21 active tools in claude-code context (~16,000 tokens) +- **Language counts**: CodeWeaver supports 166+ unique languages (27 with AST parsing, 139 with language-aware chunking) **Legend**: - βœ… **Full support/Yes** @@ -39,21 +37,59 @@ SPDX-License-Identifier: MIT OR Apache-2.0 --- +### CodeWeaver vs. Serena + +| Feature | CodeWeaver | Serena | Winner | +|---------|-----------|--------|--------| +| Search approach | Semantic (embeddings) | Symbol lookup (LSP) | **Different strengths** | +| Language support | 166+ (no LSP needed) | ~30 (requires LSP) | **CodeWeaver** (breadth) | +| Search speed | Moderate (embedding lookup) | Very fast (LSP queries) | **Serena** | +| Tool count | 1 (`find_code`) | 20+ total tools | **CodeWeaver** (simplicity) | +| Prompt overhead | ~500 tokens | ~16,000 tokens | **CodeWeaver** | +| Symbol precision | Semantic match | Exact symbols | **Serena** | +| Concept search | βœ… "authentication flow" | ❌ Must know symbol name | **CodeWeaver** | +| Editing | ❌ No | βœ… Yes | **Serena** | +| Setup requirement | Vector store + embeddings | Language server (usually running) | **Serena** (simpler) | + +**The Core Tradeoff**: + +**Serena is better when**: +- Your agent knows the symbol name or can get the information through symbol lists +- You're working in one of the ~30 supported languages +- Speed matters (LSP is instant, embeddings take time) +- You want editing capabilities too +- You don't care about prompt token overhead + +**CodeWeaver is better when**: +- **You** want to search. CodeWeaver provides you with the same tool it gives to agents. +- You/Your Agent are searching by concept: "where do we handle retries?" +- You're in an unusual language (COBOL, Fortran, legacy codebases) +- You don't have a language server running +- You care about context efficiency (16k vs 500 tokens per turn) +- You want ONE simple tool instead of choosing among 20+ + +**Real Talk**: Symbol lookup solves ~90% of code navigation needs. Serena is fast, precise, and proven. CodeWeaver targets the remaining 10% where semantic understanding matters, plus the long tail of languages LSP doesn't cover. + +> [!TIP] +> **They're complementary** +> +> Use both. If you're not concerned about prompt overhead, use Serena for "find function X" and its excellent editing capabilities, and use CodeWeaver for "find where we handle edge cases in authentication." + ## Detailed Feature Breakdown ### 1. Embedding Flexibility | Solution | Providers | Models | Customization | Code-Specific | |----------|-----------|--------|---------------|---------------| -| **CodeWeaver** | **20+** (Voyage, OpenAI, Cohere, Mistral, HuggingFace, Azure, AWS Bedrock, Google, Sentence Transformers, FastEmbed, etc.) | **50+** configured | **βœ… Plugin architecture** | βœ… Voyage Code-2, Mistral Codestral | -| Cursor | OpenAI, Custom | 1-2 | ❌ Limited | ❌ General-purpose | -| Copilot Workspace | Microsoft/OpenAI | 1 | ❌ None | ❌ General-purpose | +| **CodeWeaver** | **17** (Voyage, OpenAI, Cohere, Mistral, HuggingFace, Azure, AWS Bedrock, Google, Sentence Transformers, FastEmbed, etc.) | **50+** configured | **βœ… Plugin architecture** | βœ… Voyage Code-3, Mistral Codestral | +| Cursor | OpenAI, Custom | 1-2 | ❌ Limited | ⚠️ Unknown | +| Copilot Workspace | Microsoft/OpenAI | 1 | ❌ None | ⚠️ Unknown | | Sourcegraph Cody | None (deprecated embeddings) | 0 | N/A | N/A | | Continue.dev | Transformers.js, Ollama, Voyage, OpenAI, HuggingFace | 5-10 | ⚠️ Config-based | βœ… Voyage Code support | | Bloop | Local (unspecified) | 1 | ❌ None | ⚠️ Unknown | | Aider | None (uses repo maps) | 0 | N/A | N/A | -**CodeWeaver Advantage**: **4-10x more provider options** than any competitor +**CodeWeaver Advantage**: **4-10x more provider options** than any one else. --- @@ -70,9 +106,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Aider | N/A | N/A | N/A (graph ranking) | N/A | **Industry Context**: -- Hybrid search (dense + sparse) is industry best practice -- Voyage Code-2 with hybrid search shows **14.52% improvement** vs dense-only -- Only **CodeWeaver and Continue.dev** offer true hybrid search +- Hybrid search (dense + sparse) is industry best practice for code search +- Voyage Code-3 with hybrid search shows **14.52% improvement** vs using Voyage Code-3 alone +- **Only CodeWeaver** offers true hybrid search (both vector types with ranked results) --- @@ -80,7 +116,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Solution | AST Parsing | Languages (AST) | Fallback Method | Total Languages | |----------|-------------|----------------|-----------------|-----------------| -| **CodeWeaver** | βœ… Tree-sitter | **26** | **βœ… Intelligent delimiters (language family patterns)** | **170+** | +| **CodeWeaver** | βœ… Tree-sitter | **27** | **βœ… Language-aware chunking (family patterns)** | **166+** | | Cursor | ⚠️ Unspecified | ⚠️ Unknown | ⚠️ Unknown | ~50-100 (est.) | | Copilot Workspace | ❌ Language-agnostic | N/A | Text-based | All (basic) | | Sourcegraph Cody | ⚠️ Unspecified | ⚠️ Unknown | Keyword indexing | All | @@ -89,30 +125,29 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Aider | βœ… Tree-sitter | ~165 | βœ… ctags | ~165+ | **CodeWeaver Advantages**: -1. **Semantic-like chunking for 170+ languages** (not just line-based fallback) +1. **Language-aware chunking for 166+ languages** (not just line-based fallback) 2. **Cross-language AST normalization** (unique in market) -3. **Language family intelligence** in delimiter chunker +3. **Language family intelligence** in chunker --- -### 4. Performance & Real-Time Updates +### 4. Real-Time Updates & Indexing -| Solution | Indexing Speed | Incremental Updates | File Watching | Move Detection | -|----------|---------------|---------------------|---------------|----------------| -| **CodeWeaver** | **3.2s for 77k LOC** (index)
~5s total (remote embed)
~20s total (local embed) | **βœ… Instant** | **βœ… Live** | **βœ… Dedup on move** | -| Cursor | ⚠️ Unclear | βœ… Likely | βœ… Likely | ❌ No | -| Copilot Workspace | Server-side | ⚠️ Server-managed | ❌ Client-side | ❌ No | -| Sourcegraph Cody | ⚠️ Unclear | βœ… Yes | ⚠️ Unclear | ❌ No | -| Continue.dev | ⚠️ Unclear | βœ… Yes | βœ… Yes | ❌ No | -| Bloop | "Fast" (Rust, Tantivy, Qdrant) | ⚠️ Unclear | ⚠️ Unclear | ❌ No | -| Aider | N/A (on-demand) | ❌ No | ❌ No | N/A | +| Solution | Incremental Updates | File Watching | Move Detection | +|----------|---------------------|---------------|----------------| +| **CodeWeaver** | **βœ… Live** | **βœ… Continuous** | **βœ… Dedup on move** | +| Cursor | βœ… Likely | βœ… Likely | ❌ No | +| Copilot Workspace | ⚠️ Server-managed | ❌ Client-side | ❌ No | +| Sourcegraph Cody | βœ… Yes | ⚠️ Unclear | ❌ No | +| Continue.dev | βœ… Yes | βœ… Yes | ❌ No | +| Bloop | ⚠️ Unclear | ⚠️ Unclear | ❌ No | +| Aider | ❌ On-demand only | ❌ No | N/A | -**Benchmark Context** (Visual Studio C++ indexing): -- Gears of War: 6.5min β†’ 2.5min (2.5x speedup) -- Unreal Engine 5: 2.5min β†’ 1min (2.7x speedup) -- Chromium: 31min β†’ 5min (6x speedup) - -**CodeWeaver's sub-5s total time** for mid-size repos is competitive with industry leaders +**CodeWeaver's Real-Time Capabilities**: +- **Live file watching**: Detects changes as they happen +- **Incremental updates**: Only re-indexes changed files +- **Move detection**: Recognizes when files are moved/renamed without re-embedding +- **Continuous operation**: No manual re-indexing required --- @@ -128,17 +163,17 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Bloop | βœ… Yes | ❌ No | βœ… Yes | ⚠️ Desktop app | ❌ No | | Aider | βœ… Yes | ❌ No | βœ… Yes | βœ… CLI | N/A | -**Airgapped Deployment Leaders** (Enterprise 2024): +**Airgapped Deployment Leaders**: 1. Tabnine Enterprise (fully airgapped) 2. **CodeWeaver** (local providers + failover) 3. Sourcegraph Cody Enterprise 4. Continue.dev (via Ollama) **CodeWeaver's Intelligent Failover**: -- Primary vector store (Qdrant) fails β†’ auto-switch to in-memory store +- Primary vector store (Qdrant) fails β†’ auto-switch to in-memory store with lightweight models - Maintains live file watching during failover - Seamless handover when primary recovers -- **Zero functionality loss** (only slightly degraded results) +- **Zero functionality loss** (slightly reduced search quality only) --- @@ -167,7 +202,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Feature | CodeWeaver | Cursor | Winner | |---------|-----------|--------|--------| -| Embedding models | 50+ models, 20+ providers | 1-2 models | **CodeWeaver** | +| Embedding models | 50+ models, 17 providers | 1-2 models | **CodeWeaver** | | Hybrid search | βœ… Dense + Sparse | ❌ Dense only | **CodeWeaver** | | Privacy | βœ… Local option, airgapped | ⚠️ Sends to cloud (experimental local) | **CodeWeaver** | | IDE integration | Via MCP | Native, deep | **Cursor** | @@ -178,16 +213,19 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - **CodeWeaver**: Teams needing customization, privacy, multi-IDE support - **Cursor**: Individual developers wanting seamless IDE experience +> [!TIP] +> CodeWeaver can make Cursor better, giving it richer context through MCP. + --- ### CodeWeaver vs. Continue.dev | Feature | CodeWeaver | Continue.dev | Winner | |---------|-----------|--------------|--------| -| Embedding providers | 20+ | 4-5 | **CodeWeaver** | +| Embedding providers | 17 | 7 | **CodeWeaver** | | Embedding models | 50+ | 5-10 | **CodeWeaver** | | Hybrid search | βœ… Dense + Sparse | ⚠️ Dense + Keyword | **CodeWeaver** | -| Language support | 170+ (semantic-like) | ~165 (AST + basic fallback) | **CodeWeaver** | +| Language support | 166+ (language-aware) | ~165 (AST + basic fallback) | **CodeWeaver** | | Portability | βœ… MCP (any client) | ❌ VS Code/JetBrains only | **CodeWeaver** | | Reranking | 5 providers, 20+ models | Multiple options | **Tie** | | IDE integration | Via MCP | Native extension | **Continue.dev** | @@ -197,6 +235,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - **CodeWeaver**: Multi-IDE teams, advanced customization, MCP ecosystem - **Continue.dev**: VS Code/JetBrains users wanting embedding flexibility with native integration +> [!NOTE] +> You can use CodeWeaver **with** Continue.dev or any other MCP client. + --- ### CodeWeaver vs. Sourcegraph Cody @@ -204,8 +245,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0 | Feature | CodeWeaver | Sourcegraph Cody | Winner | |---------|-----------|------------------|--------| | Search method | Semantic (embeddings) | Keyword (deprecated embeddings) | **Different philosophies** | -| Scale | Mid-large repos | Massive repos (1M+ LOC) | **Cody** (for scale) | -| Customization | 20+ embedding providers | N/A (uses keyword search) | **CodeWeaver** | +| Scale | Mid-large repos | Massive repos (1M+ LOC) | **Cody** (proven at scale) | +| Customization | 17 embedding providers | N/A (uses keyword search) | **CodeWeaver** | | Enterprise features | Airgapped, local | Enterprise-proven, airgapped | **Tie** | | Repository understanding | Semantic chunks | Full-text + structural | **Different approaches** | @@ -229,20 +270,23 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - **CodeWeaver**: Interactive code search and discovery - **Aider**: Terminal-based AI pair programming context +> [!TIP] +> Use CodeWeaver with Aider, giving it better context using MCP! + --- ## CodeWeaver's Unique Differentiators ### βœ… Only CodeWeaver Has ALL of These: -1. **20+ embedding providers, 50+ models** (4-10x more than any competitor) +1. **17 embedding providers, 50+ models** (2-10x more than any competitor) 2. **True hybrid search** (dense + sparse embeddings by default) -3. **170+ language support** with semantic-like chunking (not just AST + line breaks) +3. **166+ language support** with language-aware chunking (not just AST + line breaks) 4. **Intelligent failover** (backup vector store with live file tracking) 5. **Cross-language AST normalization** (unique in market) 6. **Full offline/airgapped** capability with local providers 7. **MCP-native architecture** (portable, works with any MCP client) -8. **Move detection & deduplication** (doesn't re-embed moved files) +8. **Move detection & deduplication** (doesn't re-embed moved files) [experimental] --- @@ -252,8 +296,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0 |----------|---------------------|-----| | **Individual dev, wants simplicity** | Cursor, Continue.dev | Native IDE integration, easy setup | | **Enterprise, airgapped environment** | **CodeWeaver**, Sourcegraph Cody Enterprise | No external API calls, full offline capability | -| **Multi-IDE team** | **CodeWeaver** | MCP protocol works with any client | -| **Massive repos (>1M LOC)** | Sourcegraph Cody | Proven scalability, keyword search scales better | +| **Multi-IDE team** | **CodeWeaver** | MCP protocol and CLI works with any client | +| **Massive repos (>1M LOC)** | Sourcegraph Cody | Proven scalability at enterprise scale | | **Terminal-based workflow** | Aider, ripgrep | CLI-first tools | | **GitHub-centric team** | Copilot Workspace | Native GitHub integration (issues, PRs, comments) | | **Custom embedding models** | **CodeWeaver**, Continue.dev | Provider flexibility | @@ -270,53 +314,56 @@ SPDX-License-Identifier: MIT OR Apache-2.0 High Customization (Embedding Models) β–² β”‚ - β”‚ ● CodeWeaver ⭐ - β”‚ (MCP Server) + β”‚ ● CodeWeaver ⭐ + β”‚ (MCP Server) + β”‚ + β”‚ ● Continue.dev + β”‚ (IDE Extension) β”‚ - β”‚ ● Continue.dev - β”‚ (IDE Extension) + ─────┼───────────────────────────── Portability β”‚ - ─────┼───────────────────────────── β”‚ - β”‚ ● Aider ● Bloop - β”‚ (CLI) (Desktop) β”‚ - β”‚ ● Cursor ● Copilot - β”‚ (Cloud) (Cloud) + β”‚ ● Cursor ● Copilot ● Bloop + β”‚ (Cloud) (Cloud) (Desktop) β”‚ β–Ό Low Customization (1-2 Models) ``` **CodeWeaver's Position**: -- **Top tier** for embedding customization (20+ providers) -- **High portability** via MCP protocol (not IDE-locked) +- **Top tier** for embedding customization (16+ providers) +- **High portability** via MCP protocol and CLI (not IDE-locked) - **Unique combination** of flexibility + portability + airgapped capability --- ## Conclusion -**CodeWeaver leads in**: -1. Embedding provider diversity (20+ vs 1-5) +### Where CodeWeaver Leads + +1. Embedding provider diversity (17 vs 1-7) 2. Hybrid search (dense + sparse by default) -3. Language support breadth (170+ with semantic-like chunking) +3. Language support breadth (166+ with language-aware chunking) 4. Deployment flexibility (local/cloud/airgapped with failover) 5. Portability (MCP-native, not IDE-locked) -**Consider alternatives if**: +#### Consider Alternatives When... + - You want deep native IDE integration β†’ Cursor, Continue.dev - You need proven scale for massive repos β†’ Sourcegraph Cody - You prefer simplicity over customization β†’ Cursor, Bloop -**CodeWeaver is best for**: -- Teams requiring embedding customization +#### CodeWeaver is Best For... + +- Teams requiring embedding customization or wanting to tailor search and model selection - Multi-IDE environments - Privacy-sensitive/airgapped deployments -- MCP ecosystem participants +- Anyone using MCP tools regularly +- People using multiple AI clients (like Claude + Copilot) - Multi-language projects benefiting from cross-language search +- Teams working with less-common languages, or maintaining legacy codebases who want modern support --- -**Last Updated**: November 17, 2025 -**Source**: CodeWeaver Competitive Analysis Research +**Last Updated**: December 9, 2025 diff --git a/overrides/partials/languages.md b/overrides/partials/languages.md index 1f97e9a9..bd62b20f 100644 --- a/overrides/partials/languages.md +++ b/overrides/partials/languages.md @@ -37,9 +37,9 @@ - Tsx - Yaml -### Languages with Smart Delimiter Support +### Languages with Language-Aware Chunking -These languages are parsed with our delimiter-based approach. We identify patterns common to families of languages to extract meaningful semantic-like structures and data: +These languages use our language-aware chunking approach. We identify patterns common to families of languages to extract meaningful code structures: - Asciidoc - Assembly diff --git a/scripts/build/generate-docker-server-yaml.py b/scripts/build/generate-docker-server-yaml.py index 2dc94c30..86f445e8 100755 --- a/scripts/build/generate-docker-server-yaml.py +++ b/scripts/build/generate-docker-server-yaml.py @@ -165,7 +165,7 @@ def generate_server_yaml() -> dict: "title": "CodeWeaver - Code Search for AI Agents", "description": f"""Semantic code search for AI agents. Hybrid search across {len(_languages())} languages with {len(embedding_providers())} embedding providers. - Works offline with local models or cloud providers (Voyage, OpenAI, Cohere, etc.). AST-aware parsing, intelligent chunking, automatic failover. + Works offline with local models or cloud providers (Voyage, OpenAI, Cohere, etc.). AST-aware parsing, language-aware chunking, automatic failover. One simple MCP tool for any client.""", "icon": "https://github.com/knitli/codeweaver/raw/refs/heads/main/docs/assets/codeweaver-favico.png", diff --git a/scripts/build/generate-mcp-server-json.py b/scripts/build/generate-mcp-server-json.py index a7ca9583..092770ea 100755 --- a/scripts/build/generate-mcp-server-json.py +++ b/scripts/build/generate-mcp-server-json.py @@ -827,7 +827,7 @@ def _create_field_meta() -> FieldMeta: caps["search_types"] = ["semantic", "hybrid", "traditional"] # Add chunking strategies - caps["chunking_strategies"] = ["semantic", "semantically-aware-delimiters"] + caps["chunking_strategies"] = ["semantic", "language-aware"] return FieldMeta( io_modelcontextprotocol_registry_publisher_provided={ diff --git a/scripts/build/generate-supported-languages.py b/scripts/build/generate-supported-languages.py index 19809020..855ae715 100755 --- a/scripts/build/generate-supported-languages.py +++ b/scripts/build/generate-supported-languages.py @@ -18,8 +18,8 @@ import textcase from codeweaver.core.file_extensions import ALL_LANGUAGES -from codeweaver.core.language import SemanticSearchLanguage, ConfigLanguage -from codeweaver.core.types.aliases import LanguageNameT, LanguageName +from codeweaver.core.language import ConfigLanguage, SemanticSearchLanguage +from codeweaver.core.types.aliases import LanguageName, LanguageNameT ALL_LANGUAGES = frozenset(ALL_LANGUAGES) @@ -44,7 +44,7 @@ def generate_literal_type_file(languages: list[LanguageNameT], path: Path) -> No # SPDX-FileCopyrightText: 2025 Knitli Inc. # SPDX-FileContributor: Adam Poulemanos # SPDX-License-Identifier: MIT OR Apache-2.0 - \"\"\"Supported languages for CodeWeaver. These are languages that have smart delimiter-based parsing support. + \"\"\"Supported languages for CodeWeaver. These are languages that have language-aware chunking support. This file is auto-generated by scripts/build/generate_supported_languages.py Do not edit this file directly. (unless you really like Sisyphean tasks, then go for it) @@ -56,7 +56,7 @@ def generate_literal_type_file(languages: list[LanguageNameT], path: Path) -> No {generate_literal_type(languages)} - \"\"\"Literal type for supported secondary languages. These languages have pseudo-semantic parsing support using our smart delimiter-based approach. + \"\"\"Literal type for supported secondary languages. These languages have language-aware chunking support. Level of support varies by language. \"\"\" @@ -96,7 +96,7 @@ def list_item(lang: str) -> str: other_languages |= {list_item(language.as_title) for language in ConfigLanguage if not language.is_semantic_search_language} other_languages |= set(semantic_languages) # we also support these with the delimiter chunker for fallback other_languages = sorted(other_languages) - other_markdown = f"### Languages with Smart Delimiter Support\n\nThese languages are parsed with our delimiter-based approach. We identify patterns common to families of languages to extract meaningful semantic-like structures and data:\n\n{'\n'.join(other_languages)}\n\n" + other_markdown = f"### Languages with Language-Aware Chunking\n\nThese languages use our language-aware chunking approach. We identify patterns common to families of languages to extract meaningful code structures:\n\n{'\n'.join(other_languages)}\n\n" return f"## Supported Languages\n\n{semantic_markdown}\n{other_markdown}" diff --git a/server.json b/server.json deleted file mode 100644 index 64ef53eb..00000000 --- a/server.json +++ /dev/null @@ -1,1742 +0,0 @@ -{ - "name": "com.knitli/codeweaver", - "description": "Semantic code search built for AI agents. Hybrid, AST-aware, context for 166 languages.", - "repository": { - "url": "https://github.com/knitli/codeweaver", - "source": "github", - "id": "1024985391", - "subfolder": "src/codeweaver" - }, - "version": "0.1.0a5.dev48+g1c541bc8", - "websiteUrl": "https://github.com/knitli/codeweaver", - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", - "packages": [ - { - "registryType": "pypi", - "identifier": "code-weaver", - "version": "0.1.0a5.dev48+g1c541bc8", - "runtimeHint": "uvx", - "transport": { - "type": "stdio" - }, - "packageArguments": [ - { - "description": "Start the MCP server", - "isRequired": true, - "value": "server", - "isSecret": false, - "type": "positional", - "isRepeated": false - }, - { - "description": "Path to the code repository to index and search", - "isRequired": false, - "isSecret": false, - "type": "named", - "name": "--project", - "valueHint": "path", - "isRepeated": false - }, - { - "description": "Path to configuration file (TOML, YAML or JSON format). Only needed if not using default config locations.", - "isRequired": false, - "isSecret": false, - "type": "named", - "name": "--config", - "valueHint": "file_path", - "isRepeated": false - }, - { - "description": "Host address for MCP server", - "isRequired": false, - "isSecret": false, - "default": "127.0.0.1", - "type": "named", - "name": "--host", - "valueHint": "host", - "isRepeated": false - }, - { - "description": "Port for MCP server", - "isRequired": false, - "format": "number", - "isSecret": false, - "default": "9328", - "type": "named", - "name": "--port", - "valueHint": "port", - "isRepeated": false - }, - { - "description": "Transport type for MCP communication. ", - "isRequired": false, - "isSecret": false, - "default": "streamable-http", - "choices": [ - "streamable-http", - "stdio" - ], - "type": "named", - "name": "--transport", - "isRepeated": false - }, - { - "description": "Enable verbose logging with timestamps", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "type": "named", - "name": "--verbose", - "isRepeated": false - }, - { - "description": "Enable debug logging", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "type": "named", - "name": "--debug", - "isRepeated": false - } - ], - "environmentVariables": [ - { - "description": "Specify the API key for the agent provider, if required. Note: Ollama uses the `openai` client, which requires an API key. If you're using Ollama locally, you need to set this, but it can be to anything -- like `madeup-key`.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "x_ai", - "azure", - "github", - "openai", - "perplexity", - "groq", - "google", - "bedrock", - "heroku", - "fireworks", - "cerebras", - "mistral", - "ollama (cloud only)", - "cohere", - "anthropic", - "together", - "openrouter", - "moonshot", - "deepseek", - "vercel", - "litellm", - "hf_inference" - ], - "name": "CODEWEAVER_AGENT_API_KEY" - }, - { - "description": "Specify the agent model to use. Provide the model name as you would to the provider directly -- check the provider's documentation.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "claude-haiku-4.5-latest", - "name": "CODEWEAVER_AGENT_MODEL" - }, - { - "description": "Specify the agent provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "anthropic", - "choices": [ - "x_ai", - "github", - "google", - "cerebras", - "mistral", - "anthropic", - "deepseek", - "litellm", - "hf_inference", - "azure", - "openai", - "groq", - "perplexity", - "bedrock", - "heroku", - "fireworks", - "cohere", - "together", - "openrouter", - "moonshot", - "ollama", - "vercel" - ], - "name": "CODEWEAVER_AGENT_PROVIDER" - }, - { - "description": "Specify a custom config file path for CodeWeaver. Only needed if not using the default locations.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "CODEWEAVER_CONFIG_FILE" - }, - { - "description": "Specify data providers to use, separated by commas. API keys, if required, must be set using the provider's specific environment variable, such as `TAVILY_API_KEY` for the TAVILY provider.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "tavily", - "choices": [ - "tavily", - "duckduckgo" - ], - "name": "CODEWEAVER_DATA_PROVIDERS" - }, - { - "description": "Enable debug mode for CodeWeaver.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER_DEBUG" - }, - { - "description": "Specify the API key for the embedding provider, if required. Note: Ollama may require an API key if using their cloud services.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "azure", - "github", - "groq", - "mistral", - "openai", - "cohere", - "ollama (cloud only)", - "together", - "bedrock", - "google", - "heroku", - "vercel", - "voyage", - "hf_inference", - "fireworks" - ], - "name": "CODEWEAVER_EMBEDDING_API_KEY" - }, - { - "description": "Specify the embedding model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage-code-3", - "name": "CODEWEAVER_EMBEDDING_MODEL" - }, - { - "description": "Specify the embedding provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage", - "choices": [ - "vercel", - "voyage", - "azure", - "github", - "groq", - "mistral", - "openai", - "cohere", - "together", - "bedrock", - "google", - "fastembed", - "heroku", - "sentence_transformers", - "ollama", - "hf_inference", - "fireworks" - ], - "name": "CODEWEAVER_EMBEDDING_PROVIDER" - }, - { - "description": "Set the server host for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "localhost", - "name": "CODEWEAVER_HOST" - }, - { - "description": "Set the log level for CodeWeaver (e.g., DEBUG, INFO, WARNING, ERROR).", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "WARNING", - "choices": [ - "DEBUG", - "ERROR", - "WARNING", - "INFO", - "CRITICAL" - ], - "name": "CODEWEAVER_LOG_LEVEL" - }, - { - "description": "Set the MCP server port for CodeWeaver if using http transport for mcp. Not required if using the default port (9328), or stdio transport.", - "isRequired": false, - "format": "number", - "isSecret": false, - "default": "9328", - "name": "CODEWEAVER_MCP_PORT" - }, - { - "description": "Set the port for the codeweaver management server (information and management endpoints).", - "isRequired": false, - "format": "number", - "isSecret": false, - "default": "9329", - "name": "CODEWEAVER_PORT" - }, - { - "description": "Use a premade provider settings profile for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "quickstart", - "recommended", - "testing" - ], - "name": "CODEWEAVER_PROFILE" - }, - { - "description": "Set the project name for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "CODEWEAVER_PROJECT_NAME" - }, - { - "description": "Set the project path for CodeWeaver.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "CODEWEAVER_PROJECT_PATH" - }, - { - "description": "Specify the API key for the reranking provider, if required.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "bedrock", - "voyage", - "cohere" - ], - "name": "CODEWEAVER_RERANKING_API_KEY" - }, - { - "description": "Specify the reranking model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "rerank-2.5", - "name": "CODEWEAVER_RERANKING_MODEL" - }, - { - "description": "Specify the reranking provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage", - "choices": [ - "voyage", - "cohere", - "bedrock", - "fastembed", - "sentence_transformers" - ], - "name": "CODEWEAVER_RERANKING_PROVIDER" - }, - { - "description": "Specify the sparse embedding model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "prithivida/Splade_pp_en_v1", - "name": "CODEWEAVER_SPARSE_EMBEDDING_MODEL" - }, - { - "description": "Specify the sparse embedding provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "fastembed", - "choices": [ - "sentence_transformers", - "fastembed" - ], - "name": "CODEWEAVER_SPARSE_EMBEDDING_PROVIDER" - }, - { - "description": "Specify the API key for the vector store, if required.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "qdrant", - "qdrant (cloud only)" - ], - "name": "CODEWEAVER_VECTOR_STORE_API_KEY" - }, - { - "description": "Specify the port for the vector store.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "6333", - "name": "CODEWEAVER_VECTOR_STORE_PORT" - }, - { - "description": "Specify the vector store provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "qdrant", - "choices": [ - "memory", - "qdrant" - ], - "name": "CODEWEAVER_VECTOR_STORE_PROVIDER" - }, - { - "description": "Specify the URL for the vector store.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "http://localhost", - "name": "CODEWEAVER_VECTOR_STORE_URL" - }, - { - "description": "Disable telemetry data collection.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER__TELEMETRY__DISABLE_TELEMETRY" - }, - { - "description": "Opt-in to potentially identifying collection of query and search result data. This is invaluable for helping us improve CodeWeaver's search capabilities. If privacy is a higher priority, do not enable this setting.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER__TELEMETRY__TOOLS_OVER_PRIVACY" - }, - { - "description": "HTTP proxy for requests (Used by: Azure, Azure, Voyage)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HTTPS_PROXY" - }, - { - "description": "API key for OpenAI-compatible services (not necessarily an API key *for* OpenAI). The OpenAI client also requires an API key, even if you don't actually need one for your provider (like local Ollama). So provide a dummy key if needed. (Used by: Azure, Cerebras, Deepseek, Fireworks, Github, Groq, Heroku, Moonshot, Ollama, Openai, Openrouter, Perplexity, Together, Vercel, X Ai)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "OPENAI_API_KEY" - }, - { - "description": "One of: 'debug', 'info', 'warning', 'error' (Used by: Azure, Cerebras, Deepseek, Fireworks, Github, Groq, Heroku, Moonshot, Ollama, Openai, Openrouter, Perplexity, Together, Vercel, X Ai)", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "error", - "info", - "debug", - "warning" - ], - "name": "OPENAI_LOG" - }, - { - "description": "Path to the SSL certificate file for requests (Used by: Azure, Azure, Voyage)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "SSL_CERT_FILE" - }, - { - "description": "API key for Vercel service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AI_GATEWAY_API_KEY" - }, - { - "description": "AWS Account ID for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AWS_ACCOUNT_ID" - }, - { - "description": "AWS region for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AWS_REGION" - }, - { - "description": "AWS Secret Access Key for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AWS_SECRET_ACCESS_KEY" - }, - { - "description": "API key for Azure Cohere service (cohere models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AZURE_COHERE_API_KEY" - }, - { - "description": "Endpoint for Azure Cohere service (cohere models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_COHERE_ENDPOINT" - }, - { - "description": "Region for Azure Cohere service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_COHERE_REGION" - }, - { - "description": "API key for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AZURE_OPENAI_API_KEY" - }, - { - "description": "Endpoint for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_OPENAI_ENDPOINT" - }, - { - "description": "Region for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_OPENAI_REGION" - }, - { - "description": "Your Cohere API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "COHERE_API_KEY" - }, - { - "description": "Host URL for Cohere service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "CO_API_URL" - }, - { - "description": "API key for DeepSeek service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "DEEPSEEK_API_KEY" - }, - { - "description": "Your Google Gemini API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "GEMINI_API_KEY" - }, - { - "description": "Your Google API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "GOOGLE_API_KEY" - }, - { - "description": "Log level for Hugging Face Hub client", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "critical", - "info", - "debug", - "error", - "warning" - ], - "name": "HF_HUB_VERBOSITY" - }, - { - "description": "API key/token for Hugging Face service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HF_TOKEN" - }, - { - "description": "HTTP proxy for requests", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HTTPS_PROXY" - }, - { - "description": "API key for Heroku service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "INFERENCE_KEY" - }, - { - "description": "Host URL for Heroku service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "INFERENCE_URL" - }, - { - "description": "Your Mistral API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "MISTRAL_API_KEY" - }, - { - "description": "API key for OpenAI-compatible services (not necessarily an API key *for* OpenAI). The OpenAI client also requires an API key, even if you don't actually need one for your provider (like local Ollama). So provide a dummy key if needed.", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "OPENAI_API_KEY" - }, - { - "description": "One of: 'debug', 'info', 'warning', 'error'", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "error", - "info", - "debug", - "warning" - ], - "name": "OPENAI_LOG" - }, - { - "description": "Log level for Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "DEBUG", - "ERROR", - "INFO", - "WARNING" - ], - "name": "QDRANT__LOG_LEVEL" - }, - { - "description": "API key for Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "QDRANT__SERVICE__API_KEY" - }, - { - "description": "Enable TLS for Qdrant service, expects truthy or false value (e.g. 1 for on, 0 for off).", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "choices": [ - "false", - "true" - ], - "name": "QDRANT__SERVICE__ENABLE_TLS" - }, - { - "description": "Hostname of the Qdrant service; do not use for URLs with schemes (e.g. 'http://')", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "QDRANT__SERVICE__HOST" - }, - { - "description": "Port number for the Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "QDRANT__SERVICE__HTTP_PORT" - }, - { - "description": "Path to the TLS certificate file for Qdrant service. Only needed if using a self-signed certificate. If you're using qdrant-cloud, you don't need this.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "QDRANT__TLS__CERT" - }, - { - "description": "Path to the SSL certificate file for requests", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "SSL_CERT_FILE" - }, - { - "description": "Your Tavily API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "TAVILY_API_KEY" - }, - { - "description": "API key for Together service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "TOGETHER_API_KEY" - }, - { - "description": "OIDC token for Vercel service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "VERCEL_OIDC_TOKEN" - }, - { - "description": "API key for Voyage service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "VOYAGE_API_KEY" - } - ] - }, - { - "registryType": "oci", - "identifier": "docker.io/knitli/codeweaver:0.1.0alpha5.dev48+g1c541betac8", - "runtimeHint": "docker", - "transport": { - "type": "stdio" - }, - "runtimeArguments": [ - { - "description": "Automatically remove container when it exits", - "isRequired": false, - "isSecret": false, - "type": "named", - "name": "--rm", - "isRepeated": false - }, - { - "description": "Mount workspace directory as read-only", - "isRequired": false, - "value": "{workspace}:/workspace:ro", - "isSecret": false, - "variables": { - "workspace": { - "description": "Path to your codebase to index and search", - "isRequired": true, - "isSecret": false - } - }, - "type": "named", - "name": "-v", - "isRepeated": false - }, - { - "description": "Set repository path inside container", - "isRequired": false, - "value": "CODEWEAVER_PROJECT_PATH=/workspace", - "isSecret": false, - "type": "named", - "name": "-e", - "isRepeated": false - }, - { - "description": "Port mapping for MCP server", - "isRequired": false, - "value": "{host_port}:9328", - "isSecret": false, - "variables": { - "host_port": { - "description": "Host port to expose MCP server", - "isRequired": false, - "isSecret": false, - "default": "9328" - } - }, - "type": "named", - "name": "-p", - "isRepeated": false - }, - { - "description": "Docker network for Qdrant connectivity", - "isRequired": false, - "value": "{network}", - "isSecret": false, - "variables": { - "network": { - "description": "Docker network name (use 'host' for local Qdrant or custom network)", - "isRequired": false, - "isSecret": false, - "default": "bridge" - } - }, - "type": "named", - "name": "--network", - "isRepeated": false - } - ], - "packageArguments": [ - { - "description": "Start CodeWeaver MCP server", - "isRequired": false, - "value": "server", - "isSecret": false, - "type": "positional", - "isRepeated": false - }, - { - "description": "Bind to all interfaces in container", - "isRequired": false, - "value": "0.0.0.0", - "isSecret": false, - "type": "named", - "name": "--host", - "isRepeated": false - }, - { - "description": "MCP server port inside container", - "isRequired": false, - "value": "9328", - "isSecret": false, - "type": "named", - "name": "--port", - "isRepeated": false - }, - { - "description": "Use streamable-http for persistent state and continuous indexing", - "isRequired": false, - "isSecret": false, - "default": "streamable-http", - "choices": [ - "streamable-http", - "stdio" - ], - "type": "named", - "name": "--transport", - "isRepeated": false - } - ], - "environmentVariables": [ - { - "description": "Specify the API key for the agent provider, if required. Note: Ollama uses the `openai` client, which requires an API key. If you're using Ollama locally, you need to set this, but it can be to anything -- like `madeup-key`.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "x_ai", - "azure", - "github", - "openai", - "perplexity", - "groq", - "google", - "bedrock", - "heroku", - "fireworks", - "cerebras", - "mistral", - "ollama (cloud only)", - "cohere", - "anthropic", - "together", - "openrouter", - "moonshot", - "deepseek", - "vercel", - "litellm", - "hf_inference" - ], - "name": "CODEWEAVER_AGENT_API_KEY" - }, - { - "description": "Specify the agent model to use. Provide the model name as you would to the provider directly -- check the provider's documentation.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "claude-haiku-4.5-latest", - "name": "CODEWEAVER_AGENT_MODEL" - }, - { - "description": "Specify the agent provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "anthropic", - "choices": [ - "x_ai", - "github", - "google", - "cerebras", - "mistral", - "anthropic", - "deepseek", - "litellm", - "hf_inference", - "azure", - "openai", - "groq", - "perplexity", - "bedrock", - "heroku", - "fireworks", - "cohere", - "together", - "openrouter", - "moonshot", - "ollama", - "vercel" - ], - "name": "CODEWEAVER_AGENT_PROVIDER" - }, - { - "description": "Specify a custom config file path for CodeWeaver. Only needed if not using the default locations.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "CODEWEAVER_CONFIG_FILE" - }, - { - "description": "Specify data providers to use, separated by commas. API keys, if required, must be set using the provider's specific environment variable, such as `TAVILY_API_KEY` for the TAVILY provider.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "tavily", - "choices": [ - "tavily", - "duckduckgo" - ], - "name": "CODEWEAVER_DATA_PROVIDERS" - }, - { - "description": "Enable debug mode for CodeWeaver.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER_DEBUG" - }, - { - "description": "Specify the API key for the embedding provider, if required. Note: Ollama may require an API key if using their cloud services.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "azure", - "github", - "groq", - "mistral", - "openai", - "cohere", - "ollama (cloud only)", - "together", - "bedrock", - "google", - "heroku", - "vercel", - "voyage", - "hf_inference", - "fireworks" - ], - "name": "CODEWEAVER_EMBEDDING_API_KEY" - }, - { - "description": "Specify the embedding model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage-code-3", - "name": "CODEWEAVER_EMBEDDING_MODEL" - }, - { - "description": "Specify the embedding provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage", - "choices": [ - "vercel", - "voyage", - "azure", - "github", - "groq", - "mistral", - "openai", - "cohere", - "together", - "bedrock", - "google", - "fastembed", - "heroku", - "sentence_transformers", - "ollama", - "hf_inference", - "fireworks" - ], - "name": "CODEWEAVER_EMBEDDING_PROVIDER" - }, - { - "description": "Set the server host for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "localhost", - "name": "CODEWEAVER_HOST" - }, - { - "description": "Set the log level for CodeWeaver (e.g., DEBUG, INFO, WARNING, ERROR).", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "WARNING", - "choices": [ - "DEBUG", - "ERROR", - "WARNING", - "INFO", - "CRITICAL" - ], - "name": "CODEWEAVER_LOG_LEVEL" - }, - { - "description": "Set the MCP server port for CodeWeaver if using http transport for mcp. Not required if using the default port (9328), or stdio transport.", - "isRequired": false, - "format": "number", - "isSecret": false, - "default": "9328", - "name": "CODEWEAVER_MCP_PORT" - }, - { - "description": "Set the port for the codeweaver management server (information and management endpoints).", - "isRequired": false, - "format": "number", - "isSecret": false, - "default": "9329", - "name": "CODEWEAVER_PORT" - }, - { - "description": "Use a premade provider settings profile for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "quickstart", - "recommended", - "testing" - ], - "name": "CODEWEAVER_PROFILE" - }, - { - "description": "Set the project name for CodeWeaver.", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "CODEWEAVER_PROJECT_NAME" - }, - { - "description": "Set the project path for CodeWeaver.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "CODEWEAVER_PROJECT_PATH" - }, - { - "description": "Specify the API key for the reranking provider, if required.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "bedrock", - "voyage", - "cohere" - ], - "name": "CODEWEAVER_RERANKING_API_KEY" - }, - { - "description": "Specify the reranking model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "rerank-2.5", - "name": "CODEWEAVER_RERANKING_MODEL" - }, - { - "description": "Specify the reranking provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "voyage", - "choices": [ - "voyage", - "cohere", - "bedrock", - "fastembed", - "sentence_transformers" - ], - "name": "CODEWEAVER_RERANKING_PROVIDER" - }, - { - "description": "Specify the sparse embedding model to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "prithivida/Splade_pp_en_v1", - "name": "CODEWEAVER_SPARSE_EMBEDDING_MODEL" - }, - { - "description": "Specify the sparse embedding provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "fastembed", - "choices": [ - "sentence_transformers", - "fastembed" - ], - "name": "CODEWEAVER_SPARSE_EMBEDDING_PROVIDER" - }, - { - "description": "Specify the API key for the vector store, if required.", - "isRequired": false, - "format": "string", - "isSecret": true, - "choices": [ - "qdrant", - "qdrant (cloud only)" - ], - "name": "CODEWEAVER_VECTOR_STORE_API_KEY" - }, - { - "description": "Specify the port for the vector store.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "6333", - "name": "CODEWEAVER_VECTOR_STORE_PORT" - }, - { - "description": "Specify the vector store provider to use.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "qdrant", - "choices": [ - "memory", - "qdrant" - ], - "name": "CODEWEAVER_VECTOR_STORE_PROVIDER" - }, - { - "description": "Specify the URL for the vector store.", - "isRequired": false, - "format": "string", - "isSecret": false, - "default": "http://localhost", - "name": "CODEWEAVER_VECTOR_STORE_URL" - }, - { - "description": "Disable telemetry data collection.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER__TELEMETRY__DISABLE_TELEMETRY" - }, - { - "description": "Opt-in to potentially identifying collection of query and search result data. This is invaluable for helping us improve CodeWeaver's search capabilities. If privacy is a higher priority, do not enable this setting.", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "default": "false", - "choices": [ - "false", - "true" - ], - "name": "CODEWEAVER__TELEMETRY__TOOLS_OVER_PRIVACY" - }, - { - "description": "HTTP proxy for requests (Used by: Azure, Azure, Voyage)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HTTPS_PROXY" - }, - { - "description": "API key for OpenAI-compatible services (not necessarily an API key *for* OpenAI). The OpenAI client also requires an API key, even if you don't actually need one for your provider (like local Ollama). So provide a dummy key if needed. (Used by: Azure, Cerebras, Deepseek, Fireworks, Github, Groq, Heroku, Moonshot, Ollama, Openai, Openrouter, Perplexity, Together, Vercel, X Ai)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "OPENAI_API_KEY" - }, - { - "description": "One of: 'debug', 'info', 'warning', 'error' (Used by: Azure, Cerebras, Deepseek, Fireworks, Github, Groq, Heroku, Moonshot, Ollama, Openai, Openrouter, Perplexity, Together, Vercel, X Ai)", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "error", - "info", - "debug", - "warning" - ], - "name": "OPENAI_LOG" - }, - { - "description": "Path to the SSL certificate file for requests (Used by: Azure, Azure, Voyage)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "SSL_CERT_FILE" - }, - { - "description": "API key for Vercel service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AI_GATEWAY_API_KEY" - }, - { - "description": "AWS Account ID for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AWS_ACCOUNT_ID" - }, - { - "description": "AWS region for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AWS_REGION" - }, - { - "description": "AWS Secret Access Key for Bedrock service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AWS_SECRET_ACCESS_KEY" - }, - { - "description": "API key for Azure Cohere service (cohere models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AZURE_COHERE_API_KEY" - }, - { - "description": "Endpoint for Azure Cohere service (cohere models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_COHERE_ENDPOINT" - }, - { - "description": "Region for Azure Cohere service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_COHERE_REGION" - }, - { - "description": "API key for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "AZURE_OPENAI_API_KEY" - }, - { - "description": "Endpoint for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_OPENAI_ENDPOINT" - }, - { - "description": "Region for Azure OpenAI service (OpenAI models on Azure)", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "AZURE_OPENAI_REGION" - }, - { - "description": "Your Cohere API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "COHERE_API_KEY" - }, - { - "description": "Host URL for Cohere service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "CO_API_URL" - }, - { - "description": "API key for DeepSeek service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "DEEPSEEK_API_KEY" - }, - { - "description": "Your Google Gemini API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "GEMINI_API_KEY" - }, - { - "description": "Your Google API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "GOOGLE_API_KEY" - }, - { - "description": "Log level for Hugging Face Hub client", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "critical", - "info", - "debug", - "error", - "warning" - ], - "name": "HF_HUB_VERBOSITY" - }, - { - "description": "API key/token for Hugging Face service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HF_TOKEN" - }, - { - "description": "HTTP proxy for requests", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "HTTPS_PROXY" - }, - { - "description": "API key for Heroku service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "INFERENCE_KEY" - }, - { - "description": "Host URL for Heroku service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "INFERENCE_URL" - }, - { - "description": "Your Mistral API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "MISTRAL_API_KEY" - }, - { - "description": "API key for OpenAI-compatible services (not necessarily an API key *for* OpenAI). The OpenAI client also requires an API key, even if you don't actually need one for your provider (like local Ollama). So provide a dummy key if needed.", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "OPENAI_API_KEY" - }, - { - "description": "One of: 'debug', 'info', 'warning', 'error'", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "error", - "info", - "debug", - "warning" - ], - "name": "OPENAI_LOG" - }, - { - "description": "Log level for Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": false, - "choices": [ - "DEBUG", - "ERROR", - "INFO", - "WARNING" - ], - "name": "QDRANT__LOG_LEVEL" - }, - { - "description": "API key for Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "QDRANT__SERVICE__API_KEY" - }, - { - "description": "Enable TLS for Qdrant service, expects truthy or false value (e.g. 1 for on, 0 for off).", - "isRequired": false, - "format": "boolean", - "isSecret": false, - "choices": [ - "false", - "true" - ], - "name": "QDRANT__SERVICE__ENABLE_TLS" - }, - { - "description": "Hostname of the Qdrant service; do not use for URLs with schemes (e.g. 'http://')", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "QDRANT__SERVICE__HOST" - }, - { - "description": "Port number for the Qdrant service", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "QDRANT__SERVICE__HTTP_PORT" - }, - { - "description": "Path to the TLS certificate file for Qdrant service. Only needed if using a self-signed certificate. If you're using qdrant-cloud, you don't need this.", - "isRequired": false, - "format": "filepath", - "isSecret": false, - "name": "QDRANT__TLS__CERT" - }, - { - "description": "Path to the SSL certificate file for requests", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "SSL_CERT_FILE" - }, - { - "description": "Your Tavily API Key", - "isRequired": false, - "format": "string", - "isSecret": false, - "name": "TAVILY_API_KEY" - }, - { - "description": "API key for Together service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "TOGETHER_API_KEY" - }, - { - "description": "OIDC token for Vercel service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "VERCEL_OIDC_TOKEN" - }, - { - "description": "API key for Voyage service", - "isRequired": false, - "format": "string", - "isSecret": true, - "name": "VOYAGE_API_KEY" - } - ] - } - ], - "_meta": { - "io.modelcontextprotocol.registry/publisher-provided": { - "build_info": { - "framework": "fastmcp", - "package_manager": "uv", - "python_version": ">=3.12" - }, - "capabilities": { - "languages_supported": 166, - "embedding_providers": [ - "Google", - "Together", - "Hf Inference", - "Openai", - "Groq", - "Vercel", - "Azure", - "Fireworks", - "Heroku", - "Mistral", - "Sentence Transformers", - "Bedrock", - "Voyage", - "Github", - "Fastembed", - "Cohere", - "Ollama" - ], - "vector_store_providers": [ - "Memory", - "Qdrant" - ], - "sparse_embedding_providers": [ - "Fastembed", - "Sentence Transformers" - ], - "reranking_providers": [ - "Sentence Transformers", - "Bedrock", - "Voyage", - "Fastembed", - "Cohere" - ], - "search_types": [ - "semantic", - "hybrid", - "traditional" - ], - "chunking_strategies": [ - "semantic", - "semantically-aware-delimiters" - ] - }, - "tags": [ - "agent-tools", - "ast-parsing", - "code-search", - "code-understanding", - "developer-tools", - "embeddings", - "hybrid-search", - "multi-language", - "natural-language-processing", - "reranking", - "semantic-search", - "sparse-embeddings", - "vector-database" - ], - "all_supported_languages": [ - "asciidoc", - "assembly", - "assemblyscript", - "astro", - "bash (AST support)", - "batch", - "beef", - "c lang (AST support)", - "c plus plus (AST support)", - "c sharp (AST support)", - "carbon", - "chapel", - "clojure", - "cmake", - "cobol", - "coffeescript", - "confluence", - "coq", - "creole", - "crystal", - "css (AST support)", - "csv", - "cuda", - "cue", - "cython", - "dart", - "data", - "dbf", - "devicetree", - "dhall", - "dita", - "dlang", - "docbook", - "docker", - "duck", - "dyck", - "ecl", - "eiffel", - "elixir (AST support)", - "elm", - "elvish", - "emacs", - "erlang", - "eta", - "excel", - "factor", - "fortran", - "frege", - "fsharp", - "gleam", - "go (AST support)", - "gosu", - "graphql", - "groovy", - "hack", - "haskell (AST support)", - "hcl (AST support)", - "help", - "hjson", - "hlsl", - "html (AST support)", - "idris", - "imba", - "info", - "ini", - "io", - "janet", - "java (AST support)", - "javascript (AST support)", - "jelly", - "jinja", - "json (AST support)", - "jsx (AST support)", - "jule", - "julia", - "jupyter", - "just", - "kotlin (AST support)", - "lagda", - "latex", - "less", - "lhs", - "lisp", - "livescript", - "lua (AST support)", - "lucee", - "make", - "man", - "markdown", - "matlab", - "mediawiki", - "mojo", - "move", - "newick", - "nimble", - "nix (AST support)", - "nushell", - "nw", - "objective-c", - "ocaml", - "odin", - "org", - "pascal", - "perl", - "pharo", - "php (AST support)", - "pkl", - "pod", - "pony", - "powershell", - "properties", - "protobuf", - "purescript", - "python (AST support)", - "qb64", - "qml", - "r", - "racket", - "rake", - "raku", - "rakudo", - "reason", - "red", - "rescript", - "restructuredtext", - "ring", - "rmarkdown", - "rmd", - "rnw", - "rtf", - "ruby (AST support)", - "rust (AST support)", - "sas", - "sass", - "scala (AST support)", - "scheme", - "scss", - "self", - "smali", - "sml", - "solidity (AST support)", - "sql", - "svelte", - "swift (AST support)", - "texinfo", - "text", - "textile", - "toml", - "tsv", - "tsx (AST support)", - "typescript (AST support)", - "vala", - "vale", - "vbscript", - "verilog", - "vhdl", - "visualbasic6", - "vlang", - "vue", - "wiki", - "xml", - "xonsh", - "yaml (AST support)", - "yard", - "zig", - "zsh" - ] - } - } -} diff --git a/server.json.license b/server.json.license deleted file mode 100644 index 3a9c57d7..00000000 --- a/server.json.license +++ /dev/null @@ -1,4 +0,0 @@ -SPDX-FileCopyrightText: 2025 Knitli Inc. -SPDX-FileContributor: Adam Poulemanos - -SPDX-License-Identifier: MIT OR Apache-2.0 diff --git a/server.yaml b/server.yaml deleted file mode 100644 index a67ab542..00000000 --- a/server.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Knitli Inc. -# SPDX-FileContributor: Adam Poulemanos -# -# SPDX-License-Identifier: MIT OR Apache-2.0 - -name: codeweaver -image: mcp/codeweaver -type: server -meta: - category: devops - tags: - - devops -about: - title: Codeweaver - Code Search for AI Agents - description: Semantic code search built for AI agents. Advanced hybrid, AST-aware context curation for 166 languages with intelligent chunking, intent detection, and multi-provider support. Deploy CodeWeaver locally or as a service, in the cloud or completely local. Use with any IDE or AI client. CodeWeaver gives your AI agents one great tool to get the information they need. - icon: https://github.com/knitli/codeweaver/raw/refs/heads/main/docs/assets/codeweaver-favico.png -source: - project: https://github.com/knitli/codeweaver - commit: b2809e688507c84d20867de1199d00d037ca859a -run: - volumes: - - /data:{{codeweaver.data}} -config: - description: Configure the connection to Codeweaver - Code Search for AI Agents - secrets: - - name: codeweaver.foo - env: bar - example: - env: - - name: foo - example: value - value: '{{value}}' - parameters: - type: object - properties: - foo: - type: string diff --git a/src/codeweaver/core/secondary_languages.py b/src/codeweaver/core/secondary_languages.py index 5a6bbae4..8712fa87 100644 --- a/src/codeweaver/core/secondary_languages.py +++ b/src/codeweaver/core/secondary_languages.py @@ -1,7 +1,8 @@ + # SPDX-FileCopyrightText: 2025 Knitli Inc. # SPDX-FileContributor: Adam Poulemanos # SPDX-License-Identifier: MIT OR Apache-2.0 -"""Supported languages for CodeWeaver. These are languages that have smart delimiter-based parsing support. +"""Supported languages for CodeWeaver. These are languages that have language-aware chunking support. This file is auto-generated by scripts/build/generate_supported_languages.py Do not edit this file directly. (unless you really like Sisyphean tasks, then go for it) @@ -153,7 +154,7 @@ "zig", "zsh", ] -"""Literal type for supported secondary languages. These languages have pseudo-semantic parsing support using our smart delimiter-based approach. +"""Literal type for supported secondary languages. These languages have language-aware chunking support. Level of support varies by language. """ diff --git a/uv.lock b/uv.lock index 93d2df59..1ecf10b5 100644 --- a/uv.lock +++ b/uv.lock @@ -15,6 +15,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl", hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z" }, ] +[[package]] +name = "ag-ui-protocol" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/bb/5a5ec893eea5805fb9a3db76a9888c3429710dfb6f24bbb37568f2cf7320/ag_ui_protocol-0.1.10.tar.gz", hash = "sha256:3213991c6b2eb24bb1a8c362ee270c16705a07a4c5962267a083d0959ed894f4", size = 6945, upload-time = "2025-11-06T15:17:17.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/78/eb55fabaab41abc53f52c0918a9a8c0f747807e5306273f51120fd695957/ag_ui_protocol-0.1.10-py3-none-any.whl", hash = "sha256:c81e6981f30aabdf97a7ee312bfd4df0cd38e718d9fc10019c7d438128b93ab5", size = 7889, upload-time = "2025-11-06T15:17:15.325Z" }, +] + [[package]] name = "aiohappyeyeballs" version = "2.6.1" @@ -173,6 +185,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] +[[package]] +name = "argcomplete" +version = "3.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, +] + [[package]] name = "ast-grep-py" version = "0.40.0" @@ -661,7 +682,7 @@ dependencies = [ { name = "psutil" }, { name = "py-cpuinfo" }, { name = "pydantic" }, - { name = "pydantic-ai-slim" }, + { name = "pydantic-ai" }, { name = "pydantic-graph" }, { name = "pydantic-settings", extra = ["toml", "yaml"] }, { name = "qdrant-client" }, @@ -835,21 +856,22 @@ requires-dist = [ { name = "posthog", specifier = ">=7.0.1" }, { name = "psutil", specifier = ">=7.1.3" }, { name = "py-cpuinfo", specifier = ">=9.0.0" }, - { name = "py-cpuinfo", marker = "extra == 'provider-fastembed'" }, - { name = "py-cpuinfo", marker = "extra == 'provider-fastembed-gpu'" }, - { name = "py-cpuinfo", marker = "extra == 'provider-sentence-transformers'" }, + { name = "py-cpuinfo", marker = "extra == 'fastembed'" }, + { name = "py-cpuinfo", marker = "extra == 'fastembed-gpu'" }, + { name = "py-cpuinfo", marker = "extra == 'sentence-transformers'" }, { name = "pydantic", specifier = ">=2.12.5" }, { name = "pydantic-ai", specifier = ">=1.27.0" }, - { name = "pydantic-ai-slim", extras = ["anthropic"], marker = "extra == 'provider-anthropic'" }, - { name = "pydantic-ai-slim", extras = ["bedrock"], marker = "extra == 'provider-bedrock'" }, - { name = "pydantic-ai-slim", extras = ["cohere"], marker = "extra == 'provider-cohere'" }, - { name = "pydantic-ai-slim", extras = ["duckduckgo"], marker = "extra == 'source-duckduckgo'" }, - { name = "pydantic-ai-slim", extras = ["google"], marker = "extra == 'provider-google'" }, - { name = "pydantic-ai-slim", extras = ["groq"], marker = "extra == 'provider-groq'" }, - { name = "pydantic-ai-slim", extras = ["huggingface"], marker = "extra == 'provider-huggingface'" }, - { name = "pydantic-ai-slim", extras = ["mistral"], marker = "extra == 'provider-mistral'" }, - { name = "pydantic-ai-slim", extras = ["openai"], marker = "extra == 'provider-openai'" }, - { name = "pydantic-ai-slim", extras = ["tavily"], marker = "extra == 'source-tavily'" }, + { name = "pydantic-ai-slim", extras = ["anthropic"], marker = "extra == 'anthropic'" }, + { name = "pydantic-ai-slim", extras = ["anthropic", "bedrock", "cohere", "duckduckgo", "google", "groq", "huggingface", "mistral", "openai", "retries", "tavily"], marker = "extra == 'full'" }, + { name = "pydantic-ai-slim", extras = ["bedrock"], marker = "extra == 'bedrock'" }, + { name = "pydantic-ai-slim", extras = ["cohere"], marker = "extra == 'cohere'" }, + { name = "pydantic-ai-slim", extras = ["duckduckgo"], marker = "extra == 'duckduckgo'" }, + { name = "pydantic-ai-slim", extras = ["google"], marker = "extra == 'google'" }, + { name = "pydantic-ai-slim", extras = ["groq"], marker = "extra == 'groq'" }, + { name = "pydantic-ai-slim", extras = ["huggingface"], marker = "extra == 'huggingface'" }, + { name = "pydantic-ai-slim", extras = ["mistral"], marker = "extra == 'mistral'" }, + { name = "pydantic-ai-slim", extras = ["openai"], marker = "extra == 'openai'" }, + { name = "pydantic-ai-slim", extras = ["tavily"], marker = "extra == 'tavily'" }, { name = "pydantic-graph", specifier = ">=1.27.0" }, { name = "pydantic-settings", extras = ["aws-secrets-manager", "azure-key-vault", "gcp-secret-manager", "toml", "yaml"], marker = "extra == 'full'" }, { name = "pydantic-settings", extras = ["aws-secrets-manager", "azure-key-vault", "gcp-secret-manager", "toml", "yaml"], marker = "extra == 'full-gpu'" }, @@ -2304,6 +2326,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/5c/521a3d8295e2e7caea67032e65554866293b6dc8e934bd86be8cc1f7b955/langsmith-0.4.43-py3-none-any.whl", hash = "sha256:c97846a0b15061bc15844aac32fd1ce4a8e50983905f80a0d6079bb41b112ae3", size = 410232, upload-time = "2025-11-15T00:32:10.557Z" }, ] +[[package]] +name = "logfire" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "executing" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-sdk" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/60/b8040db3598a55da64c45e3e689f2baa87389a4648a6f46ba80be3329f23/logfire-4.16.0.tar.gz", hash = "sha256:03a3ab8fdc13399309cb55d69cba7a6fcbad3526cfad85fc4f72e7d75e22b654", size = 550759, upload-time = "2025-12-04T16:16:39.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/f7/ffcf81eb4aea75e40c0646b9519947d2070626c5d533922df92975045181/logfire-4.16.0-py3-none-any.whl", hash = "sha256:8f895f6c2efa593ad6d49e1b06d8e6e351d3dd0cad61ce5def0c3d401f8ea707", size = 229122, upload-time = "2025-12-04T16:16:35.963Z" }, +] + +[package.optional-dependencies] +httpx = [ + { name = "opentelemetry-instrumentation-httpx" }, +] + [[package]] name = "logfire-api" version = "4.14.2" @@ -2805,6 +2850,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, ] +[[package]] +name = "nexus-rpc" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/66/540687556bd28cf1ec370cc6881456203dfddb9dab047b8979c6865b5984/nexus_rpc-1.1.0.tar.gz", hash = "sha256:d65ad6a2f54f14e53ebe39ee30555eaeb894102437125733fb13034a04a44553", size = 77383, upload-time = "2025-07-07T19:03:58.368Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/2f/9e9d0dcaa4c6ffa22b7aa31069a8a264c753ff8027b36af602cce038c92f/nexus_rpc-1.1.0-py3-none-any.whl", hash = "sha256:d1b007af2aba186a27e736f8eaae39c03aed05b488084ff6c3d1785c9ba2ad38", size = 27743, upload-time = "2025-07-07T19:03:57.556Z" }, +] + [[package]] name = "nh3" version = "0.3.2" @@ -3115,15 +3172,124 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.38.0" +version = "1.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/d8/0f354c375628e048bd0570645b310797299754730079853095bf000fba69/opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12", size = 65242, upload-time = "2025-10-16T08:35:50.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/0b/e5428c009d4d9af0515b0a8371a8aaae695371af291f45e702f7969dce6b/opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9", size = 65763, upload-time = "2025-12-03T13:19:56.378Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/85/d831a9bc0a9e0e1a304ff3d12c1489a5fbc9bf6690a15dcbdae372bbca45/opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459", size = 66357, upload-time = "2025-12-03T13:19:33.043Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/cb/3a29ce606b10c76d413d6edd42d25a654af03e73e50696611e757d2602f3/opentelemetry_exporter_otlp_proto_common-1.39.0.tar.gz", hash = "sha256:a135fceed1a6d767f75be65bd2845da344dd8b9258eeed6bc48509d02b184409", size = 20407, upload-time = "2025-12-03T13:19:59.003Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/215edba62d13a3948c718b289539f70e40965bc37fc82ecd55bb0b749c1a/opentelemetry_exporter_otlp_proto_common-1.39.0-py3-none-any.whl", hash = "sha256:3d77be7c4bdf90f1a76666c934368b8abed730b5c6f0547a2ec57feb115849ac", size = 18367, upload-time = "2025-12-03T13:19:36.906Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/dc/1e9bf3f6a28e29eba516bc0266e052996d02bc7e92675f3cd38169607609/opentelemetry_exporter_otlp_proto_http-1.39.0.tar.gz", hash = "sha256:28d78fc0eb82d5a71ae552263d5012fa3ebad18dfd189bf8d8095ba0e65ee1ed", size = 17287, upload-time = "2025-12-03T13:20:01.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/46/e4a102e17205bb05a50dbf24ef0e92b66b648cd67db9a68865af06a242fd/opentelemetry_exporter_otlp_proto_http-1.39.0-py3-none-any.whl", hash = "sha256:5789cb1375a8b82653328c0ce13a054d285f774099faf9d068032a49de4c7862", size = 19639, upload-time = "2025-12-03T13:19:39.536Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.60b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "packaging" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/3c/bd53dbb42eff93d18e3047c7be11224aa9966ce98ac4cc5bfb860a32c95a/opentelemetry_instrumentation-0.60b0.tar.gz", hash = "sha256:4e9fec930f283a2677a2217754b40aaf9ef76edae40499c165bc7f1d15366a74", size = 31707, upload-time = "2025-12-03T13:22:00.352Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/7b/5b5b9f8cfe727a28553acf9cd287b1d7f706f5c0a00d6e482df55b169483/opentelemetry_instrumentation-0.60b0-py3-none-any.whl", hash = "sha256:aaafa1483543a402819f1bdfb06af721c87d60dd109501f9997332862a35c76a", size = 33096, upload-time = "2025-12-03T13:20:51.785Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-httpx" +version = "0.60b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/71/9dc0bc5ab14122251f520ffe9fc8dd892ca688d7d591482b5b1843d685d2/opentelemetry_instrumentation_httpx-0.60b0.tar.gz", hash = "sha256:fcf349a92fb0b941a2a18bec65141f4ba62cbf7a457a1aa580794bad44dc477c", size = 20612, upload-time = "2025-12-03T13:22:20.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/22/a340c8bfad6f31bfd6a15b0b24d5e68e05e3975e4dbdc3cea6ec4f96e060/opentelemetry_instrumentation_httpx-0.60b0-py3-none-any.whl", hash = "sha256:3f5e6fc4ddf1d9de2aaddb5255110827154dbd4de9187da906c8c2a3cc2219e9", size = 15702, upload-time = "2025-12-03T13:21:20.667Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b5/64d2f8c3393cd13ea2092106118f7b98461ba09333d40179a31444c6f176/opentelemetry_proto-1.39.0.tar.gz", hash = "sha256:c1fa48678ad1a1624258698e59be73f990b7fc1f39e73e16a9d08eef65dd838c", size = 46153, upload-time = "2025-12-03T13:20:08.729Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/4d/d500e1862beed68318705732d1976c390f4a72ca8009c4983ff627acff20/opentelemetry_proto-1.39.0-py3-none-any.whl", hash = "sha256:1e086552ac79acb501485ff0ce75533f70f3382d43d0a30728eeee594f7bf818", size = 72534, upload-time = "2025-12-03T13:19:50.251Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/e3/7cd989003e7cde72e0becfe830abff0df55c69d237ee7961a541e0167833/opentelemetry_sdk-1.39.0.tar.gz", hash = "sha256:c22204f12a0529e07aa4d985f1bca9d6b0e7b29fe7f03e923548ae52e0e15dde", size = 171322, upload-time = "2025-12-03T13:20:09.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/b4/2adc8bc83eb1055ecb592708efb6f0c520cc2eb68970b02b0f6ecda149cf/opentelemetry_sdk-1.39.0-py3-none-any.whl", hash = "sha256:90cfb07600dfc0d2de26120cebc0c8f27e69bf77cd80ef96645232372709a514", size = 132413, upload-time = "2025-12-03T13:19:51.364Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.60b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/0e/176a7844fe4e3cb5de604212094dffaed4e18b32f1c56b5258bcbcba85c2/opentelemetry_semantic_conventions-0.60b0.tar.gz", hash = "sha256:227d7aa73cbb8a2e418029d6b6465553aa01cf7e78ec9d0bc3255c7b3ac5bf8f", size = 137935, upload-time = "2025-12-03T13:20:12.395Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/56/af0306666f91bae47db14d620775604688361f0f76a872e0005277311131/opentelemetry_semantic_conventions-0.60b0-py3-none-any.whl", hash = "sha256:069530852691136018087b52688857d97bba61cd641d0f8628d2d92788c4f78a", size = 219981, upload-time = "2025-12-03T13:19:53.585Z" }, +] + +[[package]] +name = "opentelemetry-util-http" +version = "0.60b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/0d/786a713445cf338131fef3a84fab1378e4b2ef3c3ea348eeb0c915eb804a/opentelemetry_util_http-0.60b0.tar.gz", hash = "sha256:e42b7bb49bba43b6f34390327d97e5016eb1c47949ceaf37c4795472a4e3a82d", size = 10576, upload-time = "2025-12-03T13:22:41.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/a2/d86e01c28300bd41bab8f18afd613676e2bd63515417b77636fc1add426f/opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582", size = 65947, upload-time = "2025-10-16T08:35:30.23Z" }, + { url = "https://files.pythonhosted.org/packages/53/5d/a448862f6d10c95685ed0e703596b6bd1784074e7ad90bffdc550abb7b68/opentelemetry_util_http-0.60b0-py3-none-any.whl", hash = "sha256:4f366f1a48adb74ffa6f80aee26f96882e767e01b03cd1cfb948b6e1020341fe", size = 8742, upload-time = "2025-12-03T13:21:54.553Z" }, ] [[package]] @@ -3701,9 +3867,21 @@ email = [ { name = "email-validator" }, ] +[[package]] +name = "pydantic-ai" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic-ai-slim", extra = ["ag-ui", "anthropic", "bedrock", "cli", "cohere", "evals", "fastmcp", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "retries", "temporal", "ui", "vertexai"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/27/7cfe5dfbea5377cff12b84e375172d27eba6144df3063e977dbee82b002a/pydantic_ai-1.28.0.tar.gz", hash = "sha256:db0338fd5c641d7e52446953df96f9d4882efa9cb58ca27c9cd8528bcfcb7a78", size = 11812, upload-time = "2025-12-09T00:44:22.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/6e/285223cd6785b7888bbea7bd41a866d0d04c0915c5a52d2e7546bda096fb/pydantic_ai-1.28.0-py3-none-any.whl", hash = "sha256:2b955702e7a909781d8a05be0a8f22b3fa62b44e1bd3b4f3b7cf38fab6592baa", size = 7166, upload-time = "2025-12-09T00:44:14.534Z" }, +] + [[package]] name = "pydantic-ai-slim" -version = "1.27.0" +version = "1.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "genai-prices" }, @@ -3714,24 +3892,40 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/ad/bfc0a881c14cda2e41dade0a514a311343edd9916e8d1cb02414283b14b6/pydantic_ai_slim-1.27.0.tar.gz", hash = "sha256:5bfd03bff477d708c4d5479ef0f1676b5cf9daa38f30d7f1cce3d63eead5297a", size = 327922, upload-time = "2025-12-05T03:23:07.381Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/cd/3da687b2f85b6accfbb44e5192f7886796ac89f72b910af9c143da46ef14/pydantic_ai_slim-1.28.0.tar.gz", hash = "sha256:885ea6b3b33b4e9fcf3ed6edd5c9b3d69e07c5e31eed4f66fd93d579fdbbac32", size = 327953, upload-time = "2025-12-09T00:44:24.401Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/e9/e9eccab1d535414bde6c8c593dbdf685af5f355b2a1f7c6dfc4c5127a335/pydantic_ai_slim-1.27.0-py3-none-any.whl", hash = "sha256:31c1ed7dec4d59b3d95c9bb59daeb37d57f9e7d18547e30a0b7c5f5ecc44e5c0", size = 429679, upload-time = "2025-12-05T03:22:58.329Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0e/fa514e6d0ed644ee15a4255e3733792209a3b88983495aaf1dff5a9861ec/pydantic_ai_slim-1.28.0-py3-none-any.whl", hash = "sha256:ed0ce1e132df159ace38867a1cff9aa50a7ca36c146b92c49fbf39300f67a619", size = 429713, upload-time = "2025-12-09T00:44:17.479Z" }, ] [package.optional-dependencies] +ag-ui = [ + { name = "ag-ui-protocol" }, + { name = "starlette" }, +] anthropic = [ { name = "anthropic" }, ] bedrock = [ { name = "boto3" }, ] +cli = [ + { name = "argcomplete" }, + { name = "prompt-toolkit" }, + { name = "pyperclip" }, + { name = "rich" }, +] cohere = [ { name = "cohere", marker = "sys_platform != 'emscripten'" }, ] duckduckgo = [ { name = "ddgs" }, ] +evals = [ + { name = "pydantic-evals" }, +] +fastmcp = [ + { name = "fastmcp" }, +] google = [ { name = "google-genai" }, ] @@ -3741,6 +3935,12 @@ groq = [ huggingface = [ { name = "huggingface-hub", extra = ["inference"] }, ] +logfire = [ + { name = "logfire", extra = ["httpx"] }, +] +mcp = [ + { name = "mcp" }, +] mistral = [ { name = "mistralai" }, ] @@ -3753,6 +3953,16 @@ retries = [ tavily = [ { name = "tavily-python" }, ] +temporal = [ + { name = "temporalio" }, +] +ui = [ + { name = "starlette" }, +] +vertexai = [ + { name = "google-auth" }, + { name = "requests" }, +] [[package]] name = "pydantic-core" @@ -3825,9 +4035,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, ] +[[package]] +name = "pydantic-evals" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "logfire-api" }, + { name = "pydantic" }, + { name = "pydantic-ai-slim" }, + { name = "pyyaml" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/28/a3b741dcb3fac22bb608349b2fbb9b83f8a88a86ec94742cad4b9b9661e3/pydantic_evals-1.28.0.tar.gz", hash = "sha256:b31d0783ce125684b53d4bdcfda2122e9c514518b64b237e03c3fd5466638885", size = 47081, upload-time = "2025-12-09T00:44:25.404Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/05/6e873a5c3df96fc08bbd30527b28cbc75c8be24fe442628ebe7263981c2c/pydantic_evals-1.28.0-py3-none-any.whl", hash = "sha256:ab603bd26954e76e0f3acddd1c90e4555c13cced2b5e6955927c96b02294ed00", size = 56226, upload-time = "2025-12-09T00:44:19.171Z" }, +] + [[package]] name = "pydantic-graph" -version = "1.27.0" +version = "1.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3835,9 +4062,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/bf/3974e1546508461dfda9a6b1e34ba8040e1568abf03ec9a7ebc2628964f6/pydantic_graph-1.27.0.tar.gz", hash = "sha256:5762956e6b002b0415ee78fcbc60da6c7f31778760fb82dbc263f8e1bdcc5f19", size = 58366, upload-time = "2025-12-05T03:23:09.781Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/fd/8a99b6baeb888a0696b6fc36e037ad8b27abaf79f28fa474460a93af60c8/pydantic_graph-1.28.0.tar.gz", hash = "sha256:d340fe27de23343f7e475da3026881aa3f155e0c9457bfee904c581fa5f153e1", size = 58371, upload-time = "2025-12-09T00:44:26.27Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/a4/60062ac7894cc5187cb14825f4a6ca37c9203ff20a69de7a952b5c3c6f95/pydantic_graph-1.27.0-py3-none-any.whl", hash = "sha256:eea771a77516c43d5adedb337863e2cd627658dbd46e1abf2197a62b7826e738", size = 72260, upload-time = "2025-12-05T03:23:01.457Z" }, + { url = "https://files.pythonhosted.org/packages/63/15/e2ca03a93dd8e2354b7923044e60397bdf880fd8182bb82357f6bf232311/pydantic_graph-1.28.0-py3-none-any.whl", hash = "sha256:1dbbeeeaca4e93e67d0d5faed3e2ba40ee7039975cd29d09c976e8a74d9be8f4", size = 72262, upload-time = "2025-12-09T00:44:20.6Z" }, ] [[package]] @@ -4805,6 +5032,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/55/4d/e5e4c65cd66144ac3d0d5a6a2bbfba22eb6a63e6e450beba10ee8413b86d/tavily_python-0.7.13-py3-none-any.whl", hash = "sha256:911825467f2bb19b8162b4766d3e81081160a7c0fb8a15c7c716b2bef73e6296", size = 15484, upload-time = "2025-11-13T18:53:02.821Z" }, ] +[[package]] +name = "temporalio" +version = "1.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nexus-rpc" }, + { name = "protobuf" }, + { name = "types-protobuf" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/92/0775d831fa245d61b74db2059d5a24a04cef0532ed2c48310a5ab007de9c/temporalio-1.19.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c2d6d5cad8aec56e048705aa4f0bab83fec15343757ea7acf8504f2e0c289b60", size = 13175255, upload-time = "2025-11-13T22:35:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e1/2a818fefc0023eb132bfff1a03440bcaff154d4d97445ef88a40c23c20c8/temporalio-1.19.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:d85c89018cba9471ce529d90c9cee5bcc31790fd64176b9ada32cc76440f8d73", size = 12854549, upload-time = "2025-11-13T22:35:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/ff/78/fe5c8c9b112b38e01aba845335df17a8bbfd60a434ffe3c1c4737ced40a0/temporalio-1.19.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f772f0698d60f808bc3c4a055fb53e40d757fa646411845b911863eebbf0549d", size = 13237772, upload-time = "2025-11-13T22:36:00.511Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/be0fd31119651f518f8db8685fd61976d9d5bbecf3b562d51f13a6442a17/temporalio-1.19.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f706c8f49771daf342ac8daa8ed07f4124fae943177f9feef458a1255aee717c", size = 13374621, upload-time = "2025-11-13T22:36:03.431Z" }, + { url = "https://files.pythonhosted.org/packages/d8/94/18f6ae06ffd91507ded9111af1041146a5ba4b56e9256520c5ce82629fc4/temporalio-1.19.0-cp310-abi3-win_amd64.whl", hash = "sha256:162459c293553be39994f20c635a132f7332ae71bd7ba4042f8473701fcf1c7c", size = 14256891, upload-time = "2025-11-13T22:36:06.778Z" }, +] + [[package]] name = "tenacity" version = "9.1.2" @@ -5166,6 +5411,15 @@ requires-dist = [ { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] +[[package]] +name = "types-protobuf" +version = "6.32.1.20251105" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ab/0dce6a9841b5ebf3e37401879bb8cc20724ad9c770a7649bee997696cc75/types_protobuf-6.32.1.20251105.tar.gz", hash = "sha256:641002611ff87dd9fedc38a39a29cacb9907ae5ce61489b53e99ca2074bef764", size = 63846, upload-time = "2025-11-05T03:04:43.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/57/3a0d89b33b7485b7ffd99ec7cf53b0c5c89194c481f0bd673fd67e5f273f/types_protobuf-6.32.1.20251105-py3-none-any.whl", hash = "sha256:a15109d38f7cfefd2539ef86d3f93a6a41c7cad53924f8aa1a51eaddbb72a660", size = 77890, upload-time = "2025-11-05T03:04:42.067Z" }, +] + [[package]] name = "types-requests" version = "2.32.4.20250913" @@ -5462,6 +5716,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, ] +[[package]] +name = "wrapt" +version = "1.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, + { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, + { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, + { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, + { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, + { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, + { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, + { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, +] + [[package]] name = "yarl" version = "1.22.0"