chore: untrack stray local files & gitignore

Remove accidentally-committed local-only files:
- .wallet/wallet.sqlite3-shm / .wal (runtime DB sidecar files)
- AGENTS.md, TEST_SUITE_OVERVIEW.md (local agent context artifacts)

Add .wallet/, AGENTS.md, TEST_SUITE_OVERVIEW.md to .gitignore.
This commit is contained in:
redshift
2026-07-10 10:17:22 +05:30
parent 956a1ac3e1
commit b30346bcb6
5 changed files with 3 additions and 267 deletions

3
.gitignore vendored
View File

@@ -16,6 +16,9 @@ dist/
*.db-shm
*.db-wal
.*wallet.sqlite3
.wallet/
AGENTS.md
TEST_SUITE_OVERVIEW.md
*models.json
.cashu
.relay

Binary file not shown.

Binary file not shown.

View File

@@ -1,87 +0,0 @@
# Routstr Core - Agent Documentation
This folder contains detailed documentation about the Routstr project, designed for AI agents and developers to understand how the system works.
## Quick Navigation
| Document | Description |
| ---------------------------------------------------------------- | ---------------------------------------------------------- |
| **[01-project-overview.md](.agents/01-project-overview.md)** | High-level architecture, concepts, and directory structure |
| **[02-api-endpoints.md](.agents/02-api-endpoints.md)** | Complete API reference with examples |
| **[03-payment-flow.md](.agents/03-payment-flow.md)** | How Cashu payments work end-to-end |
| **[04-upstream-providers.md](.agents/04-upstream-providers.md)** | Provider architecture and routing |
| **[05-nostr-integration.md](.agents/05-nostr-integration.md)** | NIP-91 discovery and announcements |
| **[06-database-models.md](.agents/06-database-models.md)** | Database schema and SQLModel classes |
| **[07-child-keys.md](.agents/07-child-keys.md)** | Sub-account system with spending limits |
| **[08-docker-deployment.md](.agents/08-docker-deployment.md)** | Docker and production deployment |
| **[09-ui-architecture.md](.agents/09-ui-architecture.md)** | Admin dashboard structure |
## System Summary
**Routstr** is a decentralized AI inference marketplace:
- **Anyone can sell** AI inference for Bitcoin (sats)
- **Anyone can buy** using privacy-preserving eCash (Cashu)
- **No accounts, no KYC**, no central authority
### Key Technologies
- **FastAPI**: OpenAI-compatible proxy API
- **Cashu**: Private eCash payments on Bitcoin
- **Nostr**: Censorship-resistant discovery (NIP-91)
- **Next.js**: Admin dashboard
- **SQLite**: Data persistence
### Request Flow
```
1. Client pays with Cashu token (Bearer or x-cashu header)
2. Proxy validates token, reserves cost
3. Request forwarded to upstream AI (OpenAI, Anthropic, etc.)
4. Token usage calculated, balance adjusted
5. Response returned with cost info
```
### Pricing
- Fixed: flat fee per request
- Token-based: per-1K input/output tokens
- Provider fee: markup on upstream costs
- Exchange fee: BTC/USD conversion
## Quick Code Reference
### Entry Point
```python
# routstr/core/main.py
app = FastAPI()
app.include_router(proxy_router)
app.include_router(balance_router)
app.include_router(models_router)
```
### Adding a Provider
```python
# routstr/upstream/myprovider.py
class MyProvider(BaseUpstreamProvider):
provider_type = "myprovider"
def transform_model_name(self, model_id): return model_id
```
### Key Files
| File | Purpose |
| ---------------------------- | ----------------- |
| `routstr/core/main.py` | FastAPI app setup |
| `routstr/proxy.py` | Request routing |
| `routstr/auth.py` | Auth & payment |
| `routstr/wallet.py` | Cashu operations |
| `routstr/payment/price.py` | BTC pricing |
| `routstr/nostr/listing.py` | NIP-91 publish |
| `routstr/nostr/discovery.py` | Provider search |
## Version
Current: **0.4.1**

View File

@@ -1,180 +0,0 @@
# Test Suite Overview: Routstr Core
**~40 test files** split into **unit tests** (25 files) and **integration tests** (11 files + utilities). The suite covers virtually every aspect of the system.
---
## 🔐 Wallet & Authentication
| What's tested | Key files |
|---|---|
| Cashu token receive/send/credit operations | `test_wallet.py` |
| Token mint-swapping (foreign mint → primary mint) with fee estimation, melt failures, edge cases | `test_wallet.py` |
| API key creation from Cashu tokens, duplicate token handling, invalid token rejection | `test_wallet_authentication.py` |
| Authority header validation (missing, malformed, Bearer format, case insensitivity, XSS/nul-byte injection) | `test_wallet_authentication.py` |
| Wallet info endpoints (`/v1/wallet/`, `/v1/wallet/info`) — data consistency, zero balance, expired keys, key isolation | `test_wallet_information.py` |
| Top-up flow: valid tokens, multiple denominations, spent token rejection, concurrent top-ups, zero-amount edge cases, stress tests (50 sequential) | `test_wallet_topup.py` |
---
## 💰 Balance & Billing
| What's tested | Key files |
|---|---|
| Refund endpoint: x-cashu token refund, API-key refund, sat/msat unit handling, swept/collected token edge cases, 404/410/409 errors | `test_balance.py` |
| **Critical bug fix**: Balance never goes negative on cost overrun (when `tolerance_percentage` discounts the reservation and actual cost exceeds it) | `test_balance_negative_on_cost_overrun.py` |
| Concurrent cost overruns: parallel finalizations must never drive balance negative or give free inference | `test_balance_negative_on_cost_overrun.py` |
| Key validity dates, balance limits, daily reset policies, periodic reset jobs, orphan zero-balance key prevention | `test_key_logic.py` |
| Max-cost calculation: known/unknown/disabled models, tolerance percentage discounts, fixed vs token pricing | `test_payment_helpers.py` |
| `CashuTransaction` source field defaults, API-key-sourced transactions | `test_balance.py` |
---
## 🔀 Proxy & Request Routing
| What's tested | Key files |
|---|---|
| POST proxy forwarding (JSON payloads, streaming SSE, non-streaming, content-type preservation, large payloads) | `test_proxy_post_endpoints.py` |
| Model-specific endpoints, malformed JSON, insufficient balance, rate limiting, partial streaming failures | `test_proxy_post_endpoints.py` |
| Concurrent proxy requests (10 concurrent) and database state changes | `test_proxy_post_endpoints.py` |
| 404 catch-all handler: HTML for browsers, JSON for API clients, fallback when UI bundle missing | `test_proxy_not_found.py` |
| Embeddings endpoint and model case-insensitivity | `test_embeddings.py` |
| Model test endpoint: admin auth required, unsupported types rejected, oversized payloads rejected, correct upstream path used | `test_model_test_endpoint_security.py` |
---
## 🌊 Streaming & SSE
| What's tested | Key files |
|---|---|
| **Streaming SSE framing for every provider**: OpenAI-style plain, OpenRouter keepalive comments, comments glued to data chunks, JSON split across TCP boundaries, byte-by-byte fragmentation, Gemini CRLF framing, Azure leading role chunks | `test_streaming_sse_providers.py` |
| **OpenRouter regression**: `: OPENROUTER PROCESSING` keepalive comments must not crash clients with `Unexpected token ':'` | `test_streaming_sse_providers.py` |
| **Gemini combined content+usage chunk regression**: Content delivered when usage is in the same chunk | `test_streaming_sse_providers.py` |
| Mid-stream error events, model name override, multi-line non-JSON `data:` blocks with re-prefixing | `test_streaming_sse_providers.py` |
| Stream ID injection: chat completion IDs and model names injected into streamed chunks | `test_stream_id_injection.py` |
---
## 📨 Anthropic Messages (/v1/messages) Dispatch Path
| What's tested | Key files |
|---|---|
| Litellm-based dispatch for non-native providers, stripping Anthropic-only fields (`thinking`, `output_config`, `cache_control`, etc.) | `test_messages_litellm_dispatch.py` |
| Non-streaming via litellm → returns Anthropic Message response with `cost_sats` | `test_messages_litellm_dispatch.py` |
| Streaming via litellm → emits SSE events + `cost` event at end | `test_messages_litellm_dispatch.py` |
| SSE byte chunks from litellm parsed correctly (split mid-chunk, keepalive comments) | `test_messages_litellm_dispatch.py` |
| x-cashu messages dispatch: non-streaming refund, streaming refund, no-refund when fully consumed | `test_messages_litellm_dispatch.py` |
| Upstream-always-streams + aggregate-on-non-streaming for Fireworks (max_tokens > 4096 workaround) | `test_messages_litellm_dispatch.py` |
| Aggregator: text deltas → single message, tool_use `input_json_delta` concatenation, SSE byte chunk parsing | `test_messages_litellm_dispatch.py` |
| Cost accumulation across multiple message events (uses `+=` not `max()`) | `test_messages_dispatch_cost_accumulation.py` |
| Token/cost extraction, cache tokens, model name extraction/override, SSE encoding, malformed/negative cost clamping | `test_messages_dispatch_cost_accumulation.py` |
---
## 🏷️ Provider Field Injection
| What's tested | Key files |
|---|---|
| Direct upstreams get bare `provider_type`, OpenRouter gets `openrouter:UpstreamProvider`, unknown/missing becomes `"unknown"` | `test_provider_field_injection.py` |
| Idempotency: double-stamping never nests prefix (`openrouter:openrouter:Google``openrouter:Google`) | `test_provider_field_injection.py` |
| Whitespace stripping, non-string/non-dict inputs skipped, `inject_cost_metadata` also stamps provider | `test_provider_field_injection.py` |
---
## ⚙️ Upstream Providers
| What's tested | Key files |
|---|---|
| Azure: `api-key` header instead of `Authorization`, BOM-stripped API version, deployment ID path construction, base URL stripping | `test_upstream_azure.py` |
| Gemini messages: `inject_thought_signatures` for tool calls, `_openai_chunks_to_anthropic_events` translator (text, tool_use, [DONE] sentinel, blank lines) | `test_upstream_gemini.py` |
| Routstr upstream: balance RPC (auth header omitted when api_key empty, connect timeout → None), `/v1` path preservation, native messages support | `test_upstream_routstr.py` |
| Error normalization: HTML/plaintext upstream errors → JSON envelope, JSON errors pass through unchanged, empty body handling | `test_upstream_error_response.py` |
| Litellm provider prefix detection: 40+ providers from URL patterns (Fireworks, Groq, xAI, DeepSeek, Together, Perplexity, Mistral, etc.) | `test_litellm_routing.py` |
| Azure ordering beats `api.openai.com`, Ollama localhost detection, casing/trailing slash normalization, custom defaults | `test_litellm_routing.py` |
| Subclass prefix override wins over URL detection, native messages support flags | `test_messages_litellm_dispatch.py` |
---
## 💵 Cost Calculation & Caching
| What's tested | Key files |
|---|---|
| OpenAI vs Anthropic cache token formats (subtractive vs additive), cache_read exceeds prompt_tokens, malformed/boolean/float token coercion | `test_cost_calculation_caching.py` |
| Token field fallback order, missing/null usage blocks, both cache_read and cache_creation simultaneously | `test_cost_calculation_caching.py` |
| x-cashu cost injection in non-streaming/streaming responses, `cost_sats` rounding, existing usage fields preserved | `test_x_cashu_cost_sats.py` |
---
## 🧮 Token Counting
| What's tested | Key files |
|---|---|
| Local `count_tokens` shim: simple messages, litellm fallback, missing model object, empty body, malformed JSON, system prompts, Anthropic system block list, `forwarded_model_id` | `test_count_tokens_local.py` |
| Image token estimation: low/high/auto detail, small/large images, base64, multiple images, `input_image` type, no images | `test_image_tokens.py` |
| Invalid image data falls back to 512×512 defaults | `test_image_tokens.py` |
---
## 🧠 Model Prioritization Algorithm
| What's tested | Key files |
|---|---|
| Cost scores (basic, with request fee, expensive models), provider penalties (regular=1.0, OpenRouter=1.001) | `test_algorithm.py` |
| Model overrides for missing cached models, deduplication by provider identity (not provider type) | `test_algorithm.py` |
---
## 🔄 Reactive Request Correction
| What's tested | Key files |
|---|---|
| Stripping deprecated `temperature`, unsupported params from request body before retry | `test_request_correction.py` |
| No correction when param absent, label already applied, error message doesn't match, empty inputs, non-object body | `test_request_correction.py` |
| Deprecated model name NOT stripped as param, streaming 400 buffered error is correctable | `test_request_correction.py` |
| Sequential two-param correction (with `applied` set guard), immutability of input | `test_request_correction.py` |
---
## 🗄️ Database Consistency
| What's tested | Key files |
|---|---|
| Transaction atomicity: balance update rollback on failure, top-up rollback on network error | `test_database_consistency.py` |
| Concurrent balance updates via direct DB operations, race condition prevention | `test_database_consistency.py` |
| Primary key uniqueness enforced, numeric field constraints | `test_database_consistency.py` |
| Connection pooling under load (50 concurrent requests), index usage (primary key lookup < 10ms) | `test_database_consistency.py` |
---
## 🔍 Nostr Discovery & Analytics
| What's tested | Key files |
|---|---|
| Provider discovery endpoint: default format, `include_json=true`, data structure validation, NIP-91-only parsing | `test_provider_management.py` |
| No-providers, offline providers, duplicate URLs, Nostr relay failures, malformed URLs, parameter validation | `test_provider_management.py` |
| Admin routstr top-up with transient upstream failure retry | `test_provider_management.py` |
| Analytics snapshot payload: top model usage aggregation, schema/shape, fingerprint ignores `generated_at` | `test_nostr_analytics.py` |
| Analytics disable/empty-nsec skip, deduplication of unchanged payloads | `test_nostr_analytics.py` |
---
## ⚙️ Infrastructure & Settings
| What's tested | Key files |
|---|---|
| Settings seed from env, DB precedence over env, unknown key discarding, payout settings defaults and validation | `test_settings.py` |
| Periodic upstream models refresh loop: picks up providers added after startup, disabled at non-positive interval | `test_models_refresh_loop.py` |
| Logging SecurityFilter: Bearer tokens, Cashu tokens, nsec keys, API keys redacted; case insensitivity, multiple secrets, non-sensitive messages left intact | `test_logging_securityfilter.py` |
---
## 🧪 Integration Test Infrastructure
The `conftest.py` (~400 lines) provides:
- **`TestmintWallet`**: Simulated cashu mint with fallback token creation, secure token uniqueness via `secrets.token_hex` to prevent hash collisions in concurrent tests
- **`DatabaseSnapshot`**: Before/after diffing of API key state (added/modified/removed with field-level deltas)
- **App fixture**: Full FastAPI app with all wallet/proxy mocks patched in (credit_balance, send_token, recieve_token, etc.)
- **Authenticated client**: Client with persistent API key and 10k sat balance created automatically
- **WebSocket mock**: Nostr discovery patched to fail fast for performance
- **Docker vs mock mode**: Switches between real Docker services and in-memory mocks via `USE_LOCAL_SERVICES` env var