switchboard-local 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- switchboard_local-0.1.0/LICENSE +21 -0
- switchboard_local-0.1.0/PKG-INFO +270 -0
- switchboard_local-0.1.0/README.md +231 -0
- switchboard_local-0.1.0/pyproject.toml +85 -0
- switchboard_local-0.1.0/setup.cfg +4 -0
- switchboard_local-0.1.0/switchboard/__init__.py +8 -0
- switchboard_local-0.1.0/switchboard/app/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/api/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/api/admin.py +54 -0
- switchboard_local-0.1.0/switchboard/app/api/chat.py +22 -0
- switchboard_local-0.1.0/switchboard/app/api/health.py +18 -0
- switchboard_local-0.1.0/switchboard/app/api/personal.py +113 -0
- switchboard_local-0.1.0/switchboard/app/api/ui.py +387 -0
- switchboard_local-0.1.0/switchboard/app/backends/__init__.py +12 -0
- switchboard_local-0.1.0/switchboard/app/backends/base.py +27 -0
- switchboard_local-0.1.0/switchboard/app/backends/cli_agents.py +224 -0
- switchboard_local-0.1.0/switchboard/app/backends/ollama_backend.py +124 -0
- switchboard_local-0.1.0/switchboard/app/backends/registry.py +61 -0
- switchboard_local-0.1.0/switchboard/app/core/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/core/config.py +82 -0
- switchboard_local-0.1.0/switchboard/app/core/errors.py +28 -0
- switchboard_local-0.1.0/switchboard/app/core/logging.py +10 -0
- switchboard_local-0.1.0/switchboard/app/main.py +66 -0
- switchboard_local-0.1.0/switchboard/app/models/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/models/api.py +48 -0
- switchboard_local-0.1.0/switchboard/app/models/backends.py +80 -0
- switchboard_local-0.1.0/switchboard/app/models/capabilities.py +67 -0
- switchboard_local-0.1.0/switchboard/app/models/catalogue.py +107 -0
- switchboard_local-0.1.0/switchboard/app/models/internal.py +98 -0
- switchboard_local-0.1.0/switchboard/app/models/personal.py +303 -0
- switchboard_local-0.1.0/switchboard/app/models/policy.py +72 -0
- switchboard_local-0.1.0/switchboard/app/models/sessions.py +50 -0
- switchboard_local-0.1.0/switchboard/app/models/telemetry.py +271 -0
- switchboard_local-0.1.0/switchboard/app/providers/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/providers/anthropic_provider.py +66 -0
- switchboard_local-0.1.0/switchboard/app/providers/base.py +29 -0
- switchboard_local-0.1.0/switchboard/app/providers/lmstudio.py +57 -0
- switchboard_local-0.1.0/switchboard/app/providers/manual.py +27 -0
- switchboard_local-0.1.0/switchboard/app/providers/mock.py +37 -0
- switchboard_local-0.1.0/switchboard/app/providers/ollama.py +49 -0
- switchboard_local-0.1.0/switchboard/app/providers/openai_provider.py +54 -0
- switchboard_local-0.1.0/switchboard/app/providers/registry.py +38 -0
- switchboard_local-0.1.0/switchboard/app/services/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/services/answer_quality.py +284 -0
- switchboard_local-0.1.0/switchboard/app/services/capabilities.py +451 -0
- switchboard_local-0.1.0/switchboard/app/services/chat_completion.py +140 -0
- switchboard_local-0.1.0/switchboard/app/services/classifier.py +733 -0
- switchboard_local-0.1.0/switchboard/app/services/compression_layer.py +96 -0
- switchboard_local-0.1.0/switchboard/app/services/container.py +86 -0
- switchboard_local-0.1.0/switchboard/app/services/context_compression.py +149 -0
- switchboard_local-0.1.0/switchboard/app/services/core_factory.py +166 -0
- switchboard_local-0.1.0/switchboard/app/services/cost.py +36 -0
- switchboard_local-0.1.0/switchboard/app/services/deterministic_tools.py +284 -0
- switchboard_local-0.1.0/switchboard/app/services/finance_providers.py +230 -0
- switchboard_local-0.1.0/switchboard/app/services/finance_tool.py +217 -0
- switchboard_local-0.1.0/switchboard/app/services/learned_router.py +211 -0
- switchboard_local-0.1.0/switchboard/app/services/llm_router.py +175 -0
- switchboard_local-0.1.0/switchboard/app/services/local_runtime.py +165 -0
- switchboard_local-0.1.0/switchboard/app/services/news_tool.py +218 -0
- switchboard_local-0.1.0/switchboard/app/services/personal_switchboard.py +1338 -0
- switchboard_local-0.1.0/switchboard/app/services/policy_engine.py +109 -0
- switchboard_local-0.1.0/switchboard/app/services/provider_status.py +20 -0
- switchboard_local-0.1.0/switchboard/app/services/response_sanitizer.py +133 -0
- switchboard_local-0.1.0/switchboard/app/services/router.py +224 -0
- switchboard_local-0.1.0/switchboard/app/services/runtime_context.py +70 -0
- switchboard_local-0.1.0/switchboard/app/services/semantic_memory.py +240 -0
- switchboard_local-0.1.0/switchboard/app/services/sensitivity_escalator.py +128 -0
- switchboard_local-0.1.0/switchboard/app/services/session_context.py +199 -0
- switchboard_local-0.1.0/switchboard/app/services/status_intents.py +56 -0
- switchboard_local-0.1.0/switchboard/app/services/switchboard_core.py +1301 -0
- switchboard_local-0.1.0/switchboard/app/services/telemetry.py +80 -0
- switchboard_local-0.1.0/switchboard/app/services/tool_dispatcher.py +170 -0
- switchboard_local-0.1.0/switchboard/app/services/tools.py +319 -0
- switchboard_local-0.1.0/switchboard/app/services/web_search_providers.py +93 -0
- switchboard_local-0.1.0/switchboard/app/services/web_search_tool.py +144 -0
- switchboard_local-0.1.0/switchboard/app/storage/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/storage/db.py +71 -0
- switchboard_local-0.1.0/switchboard/app/storage/repositories.py +669 -0
- switchboard_local-0.1.0/switchboard/app/utils/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/app/utils/ids.py +7 -0
- switchboard_local-0.1.0/switchboard/app/utils/redaction.py +37 -0
- switchboard_local-0.1.0/switchboard/app/utils/secret_patterns.py +95 -0
- switchboard_local-0.1.0/switchboard/app/utils/time.py +11 -0
- switchboard_local-0.1.0/switchboard/cli.py +1484 -0
- switchboard_local-0.1.0/switchboard/config/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/config/models.yaml +295 -0
- switchboard_local-0.1.0/switchboard/config/personal.example.yaml +117 -0
- switchboard_local-0.1.0/switchboard/config/personal.yaml +117 -0
- switchboard_local-0.1.0/switchboard/config/policies.yaml +49 -0
- switchboard_local-0.1.0/switchboard/config/router_weights.json +4645 -0
- switchboard_local-0.1.0/switchboard/config/sensitivity_weights.json +3101 -0
- switchboard_local-0.1.0/switchboard/config/tool_dispatcher_weights.json +7733 -0
- switchboard_local-0.1.0/switchboard/evals/__init__.py +12 -0
- switchboard_local-0.1.0/switchboard/evals/datasets.py +864 -0
- switchboard_local-0.1.0/switchboard/evals/mock_adapters.py +129 -0
- switchboard_local-0.1.0/switchboard/evals/quality_bench.py +501 -0
- switchboard_local-0.1.0/switchboard/evals/quality_dataset.py +1776 -0
- switchboard_local-0.1.0/switchboard/evals/real_providers.py +183 -0
- switchboard_local-0.1.0/switchboard/evals/real_smoke.py +473 -0
- switchboard_local-0.1.0/switchboard/evals/reports.py +146 -0
- switchboard_local-0.1.0/switchboard/evals/runner.py +374 -0
- switchboard_local-0.1.0/switchboard/evals/scorers.py +47 -0
- switchboard_local-0.1.0/switchboard/evals/types.py +155 -0
- switchboard_local-0.1.0/switchboard/training/__init__.py +1 -0
- switchboard_local-0.1.0/switchboard/training/augment.py +104 -0
- switchboard_local-0.1.0/switchboard/training/external_datasets.py +253 -0
- switchboard_local-0.1.0/switchboard/training/feedback_loop.py +458 -0
- switchboard_local-0.1.0/switchboard/training/router_dataset.py +480 -0
- switchboard_local-0.1.0/switchboard/training/sensitivity_dataset.py +166 -0
- switchboard_local-0.1.0/switchboard/training/tool_dispatcher_dataset.py +224 -0
- switchboard_local-0.1.0/switchboard/training/train_router.py +258 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/PKG-INFO +270 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/SOURCES.txt +143 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/dependency_links.txt +1 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/entry_points.txt +3 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/requires.txt +18 -0
- switchboard_local-0.1.0/switchboard_local.egg-info/top_level.txt +1 -0
- switchboard_local-0.1.0/tests/test_architecture_fixes.py +403 -0
- switchboard_local-0.1.0/tests/test_capability_strength.py +344 -0
- switchboard_local-0.1.0/tests/test_chat_api.py +211 -0
- switchboard_local-0.1.0/tests/test_classifier.py +254 -0
- switchboard_local-0.1.0/tests/test_config_paths.py +100 -0
- switchboard_local-0.1.0/tests/test_context_compression.py +207 -0
- switchboard_local-0.1.0/tests/test_cost.py +37 -0
- switchboard_local-0.1.0/tests/test_dogfood_regressions.py +819 -0
- switchboard_local-0.1.0/tests/test_evals.py +468 -0
- switchboard_local-0.1.0/tests/test_external_datasets.py +180 -0
- switchboard_local-0.1.0/tests/test_feedback_loop.py +419 -0
- switchboard_local-0.1.0/tests/test_learned_router.py +275 -0
- switchboard_local-0.1.0/tests/test_live_tools.py +294 -0
- switchboard_local-0.1.0/tests/test_local_runtime.py +78 -0
- switchboard_local-0.1.0/tests/test_paper_components.py +437 -0
- switchboard_local-0.1.0/tests/test_personal_api.py +450 -0
- switchboard_local-0.1.0/tests/test_personal_daily_use.py +1167 -0
- switchboard_local-0.1.0/tests/test_personal_providers.py +99 -0
- switchboard_local-0.1.0/tests/test_policy_engine.py +80 -0
- switchboard_local-0.1.0/tests/test_provider_mock.py +39 -0
- switchboard_local-0.1.0/tests/test_quality_bench.py +146 -0
- switchboard_local-0.1.0/tests/test_router.py +132 -0
- switchboard_local-0.1.0/tests/test_session_context.py +694 -0
- switchboard_local-0.1.0/tests/test_switchboard_capabilities.py +966 -0
- switchboard_local-0.1.0/tests/test_switchboard_core.py +1142 -0
- switchboard_local-0.1.0/tests/test_tool_dispatcher.py +407 -0
- switchboard_local-0.1.0/tests/test_ui_api.py +492 -0
- switchboard_local-0.1.0/tests/test_ui_v2.py +169 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vinay Gupta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: switchboard-local
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Privacy-aware, local-first router across CLI coding agents (Codex, Claude Code) and local LLMs (Ollama).
|
|
5
|
+
Author-email: Vinay Gupta <ai.vinaygupta@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aivinay/switchboard
|
|
8
|
+
Project-URL: Repository, https://github.com/aivinay/switchboard
|
|
9
|
+
Project-URL: Issues, https://github.com/aivinay/switchboard/issues
|
|
10
|
+
Keywords: llm,llm-routing,local-first,privacy,ollama,claude-code,codex,ai-agents,orchestration,on-device
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: fastapi>=0.115.0
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: pydantic>=2.8.0
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
27
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
28
|
+
Requires-Dist: sqlmodel>=0.0.22
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mypy>=1.11.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest>=8.3.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
34
|
+
Provides-Extra: finance
|
|
35
|
+
Requires-Dist: yfinance>=0.2.50; extra == "finance"
|
|
36
|
+
Provides-Extra: router
|
|
37
|
+
Requires-Dist: numpy>=1.26; extra == "router"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
<h1 align="center">Switchboard</h1>
|
|
41
|
+
|
|
42
|
+
<p align="center"><strong>A privacy-aware, local-first router across your CLI coding agents and local LLMs.</strong></p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://github.com/aivinay/switchboard/actions/workflows/ci.yml"><img src="https://github.com/aivinay/switchboard/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
46
|
+
<a href="RELEASE.md"><img src="https://img.shields.io/badge/PyPI-pending-lightgrey.svg" alt="PyPI: pending"></a>
|
|
47
|
+
<img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
|
|
48
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
|
|
49
|
+
<a href="https://doi.org/10.5281/zenodo.20789935"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.20789935-blue.svg" alt="DOI"></a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<a href="#get-started-60-seconds">Install</a> ·
|
|
54
|
+
<a href="#how-it-works">How it works</a> ·
|
|
55
|
+
<a href="#context-memory-and-tokens">Context</a> ·
|
|
56
|
+
<a href="#proof">Proof</a> ·
|
|
57
|
+
<a href="#privacy">Privacy</a> ·
|
|
58
|
+
<a href="#the-paper">Paper</a> ·
|
|
59
|
+
<a href="docs/">Docs</a>
|
|
60
|
+
</p>
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
> Switchboard routes prompts to the right model while preserving context through
|
|
65
|
+
> local semantic memory and context compression, keeping sensitive work local,
|
|
66
|
+
> and reducing unnecessary premium-model usage.
|
|
67
|
+
|
|
68
|
+
It's built for the single-workstation setup where the scarce resources aren't
|
|
69
|
+
dollars-per-token but **subscription quota**, **privacy**, and a pile of
|
|
70
|
+
**heterogeneous agent interfaces**.
|
|
71
|
+
|
|
72
|
+
## What it does
|
|
73
|
+
|
|
74
|
+
- **Routes** across local [Ollama](https://ollama.com) models, the **Codex** CLI, and **Claude Code** — deterministic rules first, with optional tiny learned classifiers for recall.
|
|
75
|
+
- **Private mode** — a deterministic keyword/PII/secret-format floor blocks sensitive prompts from ever reaching a subscription backend, even on fallback.
|
|
76
|
+
- **Grounds** answers with deterministic tools (time/date, safe calculator, unit conversion, keyless live stock & news) instead of letting a model guess.
|
|
77
|
+
- **Carries context** across backend switches: recent user, assistant, and tool turns are assembled into one redacted session prompt.
|
|
78
|
+
- **Compresses** long context with a Headroom-inspired layer; the model-boundary pass only summarizes recent conversation, while trusted facts, retrieved memory, and the current request survive intact.
|
|
79
|
+
- **Remembers** across backends via local embedding-based semantic memory, with SQLite search available for direct memory lookup.
|
|
80
|
+
- **Explains every decision** and records metadata-only telemetry (no prompt/response bodies).
|
|
81
|
+
- **Ships its own evaluation** — a 100-case quality benchmark, a local LLM-as-judge, and a multi-run statistical harness.
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
UI / CLI ──► Session manager (shared history across all backends)
|
|
87
|
+
│
|
|
88
|
+
▼
|
|
89
|
+
Capability detector (regex) ◄──► deterministic tools
|
|
90
|
+
│ (learned tool dispatcher recovers misses; tool verifies)
|
|
91
|
+
▼
|
|
92
|
+
Privacy floor (keywords + PII + secret formats — a match is FINAL)
|
|
93
|
+
│ (learned sensitivity escalator may only ADD protection)
|
|
94
|
+
▼
|
|
95
|
+
Deterministic policy ← always wins; unknown ⇒ local
|
|
96
|
+
│ (learned router supplies recall: tool / local / coding / reasoning)
|
|
97
|
+
▼
|
|
98
|
+
Context builder + redaction ◄── semantic memory
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
Compression (metadata + history-only context pass)
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
Ollama (default) │ Codex (coding) │ Claude Code (reasoning)
|
|
105
|
+
│
|
|
106
|
+
▼
|
|
107
|
+
Response sanitizer ─► metadata-only telemetry
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The organizing invariant: **deterministic policy always precedes and overrides
|
|
111
|
+
the learned components.** Privacy, tool grounding, forced selection, and
|
|
112
|
+
fallback keep working even when the local model runtime — and therefore every
|
|
113
|
+
learned component — is down.
|
|
114
|
+
|
|
115
|
+
## Get started (60 seconds)
|
|
116
|
+
|
|
117
|
+
PyPI release is pending. Until the first release is published:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install "git+https://github.com/aivinay/switchboard.git"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
After the PyPI release:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install switchboard-local
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# point it at a local model runtime (install Ollama, then pull a small model)
|
|
131
|
+
ollama pull llama3.2:3b
|
|
132
|
+
|
|
133
|
+
# sanity-check your setup
|
|
134
|
+
switchboard doctor
|
|
135
|
+
|
|
136
|
+
# ask — Switchboard routes it, grounds it, and tells you why
|
|
137
|
+
switchboard ask "summarize this error log and suggest a fix"
|
|
138
|
+
|
|
139
|
+
# see the routing decision without running anything
|
|
140
|
+
switchboard route "refactor the auth module and add tests"
|
|
141
|
+
|
|
142
|
+
# prefer your browser? launch the local web UI, then open http://127.0.0.1:8080/ui
|
|
143
|
+
switchboard ui
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Requires **Python 3.11+**. Codex / Claude Code backends are optional — without
|
|
147
|
+
them, everything routes locally. See [docs/usage.md](docs/usage.md).
|
|
148
|
+
|
|
149
|
+
## Context, memory, and tokens
|
|
150
|
+
|
|
151
|
+
Switchboard has two user-facing CLI surfaces:
|
|
152
|
+
|
|
153
|
+
- `switchboard route ...` and bare `switchboard ask ...` use the personal local-first route/call workflow.
|
|
154
|
+
- The web UI and `switchboard ask --backend auto ...` use the stateful core workflow: shared sessions, model switching, semantic-memory retrieval, context-boundary compression, and backend telemetry all run on the same path.
|
|
155
|
+
|
|
156
|
+
Example stateful CLI session:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
switchboard ask --backend auto --new-session "Remember: prefer local models for private notes."
|
|
160
|
+
switchboard ask --backend auto --session <session_id> --memory "What should you remember?"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Long prompts and long sessions record token estimates and savings metadata. The request-level pass can shorten an oversized raw prompt; the context-boundary pass then compresses only `<recent_conversation>`. The `<trusted_facts>`, `<long_term_memory>`, and `<current_user_request>` blocks are protected from that second pass so grounding and intent are not traded away for token budget.
|
|
164
|
+
|
|
165
|
+
Memory is local. `switchboard memory add` stores the item in SQLite and, when `semantic_memory_enabled` is on and Ollama can serve `nomic-embed-text`, indexes an embedding for cross-backend retrieval. `switchboard memory search` works as local text search even when embeddings are unavailable.
|
|
166
|
+
|
|
167
|
+
Details: [docs/context-memory-compression.md](docs/context-memory-compression.md).
|
|
168
|
+
|
|
169
|
+
## Proof
|
|
170
|
+
|
|
171
|
+
A 100-case benchmark across five task categories (coding, reasoning,
|
|
172
|
+
summarization, private, grounding), run on real backends and judged by a local
|
|
173
|
+
model, over **multiple independent runs** (means shown; full per-condition
|
|
174
|
+
numbers, confidence intervals, and significance tests are in the paper):
|
|
175
|
+
|
|
176
|
+
| Policy | Quality (1–5) | Premium usage | Privacy leaks | Answered |
|
|
177
|
+
|-------------------|:-------------:|:-------------:|:-------------:|:--------:|
|
|
178
|
+
| always-local | 3.4 | 0% | **0** | 100% |
|
|
179
|
+
| rules | 3.8 | 27% | **0** | 100% |
|
|
180
|
+
| hybrid | 3.9 | 28% | **0** | 100% |
|
|
181
|
+
| **learned** | **4.1** | 38% | **0** | 100% |
|
|
182
|
+
| always-premium | 4.6 | 100% | **0** | 61%¹ |
|
|
183
|
+
|
|
184
|
+
<sub>¹ The "just use the premium agent for everything" baseline must <em>block</em> every
|
|
185
|
+
sensitive prompt to stay leak-free, so its coverage collapses — exactly the gap
|
|
186
|
+
Switchboard closes. <strong>Zero measured leaks in every condition and every run.</strong></sub>
|
|
187
|
+
|
|
188
|
+
These numbers come from a real-backend benchmark whose full harness travels with the paper's [reproduction bundle on Zenodo](https://doi.org/10.5281/zenodo.20789935).
|
|
189
|
+
|
|
190
|
+
## Privacy
|
|
191
|
+
|
|
192
|
+
Switchboard is local-first and privacy-aware by construction:
|
|
193
|
+
|
|
194
|
+
- The **deterministic privacy floor runs before any non-local routing**; a positive verdict is final and cannot be overridden by a learned component or by prompt wording.
|
|
195
|
+
- **Secret-format detection** (cloud keys, JWTs, PEM blocks, env credentials) shares its patterns with context redaction, so the routing boundary and the redactor can't drift apart.
|
|
196
|
+
- **Metadata-only telemetry** — prompt and response bodies are not stored by default.
|
|
197
|
+
- Semantic-memory **embeddings and the eval judge run locally**.
|
|
198
|
+
|
|
199
|
+
Switchboard deliberately does **not** resell API access, scrape web UIs, or
|
|
200
|
+
bypass provider limits — subscription CLIs are invoked exactly as the
|
|
201
|
+
authenticated user could invoke them, in read-only sandbox modes. See
|
|
202
|
+
[SECURITY.md](SECURITY.md) and [docs/privacy.md](docs/privacy.md).
|
|
203
|
+
|
|
204
|
+
<details>
|
|
205
|
+
<summary><b>What's inside</b></summary>
|
|
206
|
+
|
|
207
|
+
- **Deterministic router** — keyword rules; unknown prompts default local-first.
|
|
208
|
+
- **Learned router / tool dispatcher / sensitivity escalator** — tiny softmax classifiers over a locally-computed embedding (~50 ms, pure-Python inference), each retrainable in seconds from your own thumbs-down corrections behind golden-accuracy gates. They fail closed to the deterministic path.
|
|
209
|
+
- **Tools** — time/date with timezones, safe abstract-syntax-tree calculator, unit conversion, keyless live stock quotes & news.
|
|
210
|
+
- **Compression** — structure-aware, deterministic, dependency-free; preserves task header, code blocks, tracebacks, and grounded facts.
|
|
211
|
+
- **Semantic memory** — `nomic-embed-text` embeddings, cosine retrieval, local memory commands, and SQLite text-search fallback for direct search.
|
|
212
|
+
- **Evaluation** — mock evals (CI), real-backend smoke suite, 100-case quality benchmark, adversarial tester/developer dogfooding loop.
|
|
213
|
+
|
|
214
|
+
</details>
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
Settings live in `config/personal.yaml` (ships with safe local-first defaults —
|
|
219
|
+
see `config/personal.example.yaml`). Highlights:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
preferences:
|
|
223
|
+
router_mode: "learned" # rules | llm | hybrid | learned
|
|
224
|
+
private_mode: true # block sensitive prompts from non-local backends
|
|
225
|
+
allow_cloud: false
|
|
226
|
+
compression_enabled: true
|
|
227
|
+
compression_threshold_tokens: 1000
|
|
228
|
+
semantic_memory_enabled: true
|
|
229
|
+
semantic_memory_top_k: 3
|
|
230
|
+
finance_provider: "yahoo"
|
|
231
|
+
news_provider: "google_news_rss"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Provider API keys are referenced **by environment-variable name** (e.g.
|
|
235
|
+
`OPENAI_API_KEY`), never inline. See [docs/overrides.md](docs/overrides.md).
|
|
236
|
+
|
|
237
|
+
## The paper
|
|
238
|
+
|
|
239
|
+
Switchboard is described in a preprint — *"Privacy-Aware Hybrid Routing Across
|
|
240
|
+
Heterogeneous AI Agents on a Single Workstation."* The manuscript, the multi-run
|
|
241
|
+
benchmark harness, the statistical-aggregation and figure scripts, and the
|
|
242
|
+
per-case records are archived together as a reproduction bundle on Zenodo:
|
|
243
|
+
[10.5281/zenodo.20789935](https://doi.org/10.5281/zenodo.20789935).
|
|
244
|
+
|
|
245
|
+
This repository ships only the software. It deliberately does not carry the
|
|
246
|
+
paper's experiment-running or figure-generation tooling — that lives with the
|
|
247
|
+
archival record so the code stays focused on the router itself.
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
make install # .venv + editable install with dev extras
|
|
253
|
+
make check # ruff + mypy + the full test suite
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Issues and PRs welcome — please preserve
|
|
257
|
+
the privacy invariant described there.
|
|
258
|
+
|
|
259
|
+
## Citing Switchboard
|
|
260
|
+
|
|
261
|
+
A preprint is available on Zenodo with a citable DOI —
|
|
262
|
+
[10.5281/zenodo.20789935](https://doi.org/10.5281/zenodo.20789935). See
|
|
263
|
+
[CITATION.cff](CITATION.cff) for machine-readable metadata.
|
|
264
|
+
|
|
265
|
+
> V. Gupta, "Switchboard: Privacy-Aware Hybrid Routing Across Heterogeneous AI
|
|
266
|
+
> Agents on a Single Workstation," Zenodo, 2026, doi:10.5281/zenodo.20789935.
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
[MIT](LICENSE) © 2026 Vinay Gupta
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
<h1 align="center">Switchboard</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center"><strong>A privacy-aware, local-first router across your CLI coding agents and local LLMs.</strong></p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/aivinay/switchboard/actions/workflows/ci.yml"><img src="https://github.com/aivinay/switchboard/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
7
|
+
<a href="RELEASE.md"><img src="https://img.shields.io/badge/PyPI-pending-lightgrey.svg" alt="PyPI: pending"></a>
|
|
8
|
+
<img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
|
|
10
|
+
<a href="https://doi.org/10.5281/zenodo.20789935"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.20789935-blue.svg" alt="DOI"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="#get-started-60-seconds">Install</a> ·
|
|
15
|
+
<a href="#how-it-works">How it works</a> ·
|
|
16
|
+
<a href="#context-memory-and-tokens">Context</a> ·
|
|
17
|
+
<a href="#proof">Proof</a> ·
|
|
18
|
+
<a href="#privacy">Privacy</a> ·
|
|
19
|
+
<a href="#the-paper">Paper</a> ·
|
|
20
|
+
<a href="docs/">Docs</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
> Switchboard routes prompts to the right model while preserving context through
|
|
26
|
+
> local semantic memory and context compression, keeping sensitive work local,
|
|
27
|
+
> and reducing unnecessary premium-model usage.
|
|
28
|
+
|
|
29
|
+
It's built for the single-workstation setup where the scarce resources aren't
|
|
30
|
+
dollars-per-token but **subscription quota**, **privacy**, and a pile of
|
|
31
|
+
**heterogeneous agent interfaces**.
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
- **Routes** across local [Ollama](https://ollama.com) models, the **Codex** CLI, and **Claude Code** — deterministic rules first, with optional tiny learned classifiers for recall.
|
|
36
|
+
- **Private mode** — a deterministic keyword/PII/secret-format floor blocks sensitive prompts from ever reaching a subscription backend, even on fallback.
|
|
37
|
+
- **Grounds** answers with deterministic tools (time/date, safe calculator, unit conversion, keyless live stock & news) instead of letting a model guess.
|
|
38
|
+
- **Carries context** across backend switches: recent user, assistant, and tool turns are assembled into one redacted session prompt.
|
|
39
|
+
- **Compresses** long context with a Headroom-inspired layer; the model-boundary pass only summarizes recent conversation, while trusted facts, retrieved memory, and the current request survive intact.
|
|
40
|
+
- **Remembers** across backends via local embedding-based semantic memory, with SQLite search available for direct memory lookup.
|
|
41
|
+
- **Explains every decision** and records metadata-only telemetry (no prompt/response bodies).
|
|
42
|
+
- **Ships its own evaluation** — a 100-case quality benchmark, a local LLM-as-judge, and a multi-run statistical harness.
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
UI / CLI ──► Session manager (shared history across all backends)
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
Capability detector (regex) ◄──► deterministic tools
|
|
51
|
+
│ (learned tool dispatcher recovers misses; tool verifies)
|
|
52
|
+
▼
|
|
53
|
+
Privacy floor (keywords + PII + secret formats — a match is FINAL)
|
|
54
|
+
│ (learned sensitivity escalator may only ADD protection)
|
|
55
|
+
▼
|
|
56
|
+
Deterministic policy ← always wins; unknown ⇒ local
|
|
57
|
+
│ (learned router supplies recall: tool / local / coding / reasoning)
|
|
58
|
+
▼
|
|
59
|
+
Context builder + redaction ◄── semantic memory
|
|
60
|
+
│
|
|
61
|
+
▼
|
|
62
|
+
Compression (metadata + history-only context pass)
|
|
63
|
+
│
|
|
64
|
+
▼
|
|
65
|
+
Ollama (default) │ Codex (coding) │ Claude Code (reasoning)
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
Response sanitizer ─► metadata-only telemetry
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The organizing invariant: **deterministic policy always precedes and overrides
|
|
72
|
+
the learned components.** Privacy, tool grounding, forced selection, and
|
|
73
|
+
fallback keep working even when the local model runtime — and therefore every
|
|
74
|
+
learned component — is down.
|
|
75
|
+
|
|
76
|
+
## Get started (60 seconds)
|
|
77
|
+
|
|
78
|
+
PyPI release is pending. Until the first release is published:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install "git+https://github.com/aivinay/switchboard.git"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
After the PyPI release:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install switchboard-local
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# point it at a local model runtime (install Ollama, then pull a small model)
|
|
92
|
+
ollama pull llama3.2:3b
|
|
93
|
+
|
|
94
|
+
# sanity-check your setup
|
|
95
|
+
switchboard doctor
|
|
96
|
+
|
|
97
|
+
# ask — Switchboard routes it, grounds it, and tells you why
|
|
98
|
+
switchboard ask "summarize this error log and suggest a fix"
|
|
99
|
+
|
|
100
|
+
# see the routing decision without running anything
|
|
101
|
+
switchboard route "refactor the auth module and add tests"
|
|
102
|
+
|
|
103
|
+
# prefer your browser? launch the local web UI, then open http://127.0.0.1:8080/ui
|
|
104
|
+
switchboard ui
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Requires **Python 3.11+**. Codex / Claude Code backends are optional — without
|
|
108
|
+
them, everything routes locally. See [docs/usage.md](docs/usage.md).
|
|
109
|
+
|
|
110
|
+
## Context, memory, and tokens
|
|
111
|
+
|
|
112
|
+
Switchboard has two user-facing CLI surfaces:
|
|
113
|
+
|
|
114
|
+
- `switchboard route ...` and bare `switchboard ask ...` use the personal local-first route/call workflow.
|
|
115
|
+
- The web UI and `switchboard ask --backend auto ...` use the stateful core workflow: shared sessions, model switching, semantic-memory retrieval, context-boundary compression, and backend telemetry all run on the same path.
|
|
116
|
+
|
|
117
|
+
Example stateful CLI session:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
switchboard ask --backend auto --new-session "Remember: prefer local models for private notes."
|
|
121
|
+
switchboard ask --backend auto --session <session_id> --memory "What should you remember?"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Long prompts and long sessions record token estimates and savings metadata. The request-level pass can shorten an oversized raw prompt; the context-boundary pass then compresses only `<recent_conversation>`. The `<trusted_facts>`, `<long_term_memory>`, and `<current_user_request>` blocks are protected from that second pass so grounding and intent are not traded away for token budget.
|
|
125
|
+
|
|
126
|
+
Memory is local. `switchboard memory add` stores the item in SQLite and, when `semantic_memory_enabled` is on and Ollama can serve `nomic-embed-text`, indexes an embedding for cross-backend retrieval. `switchboard memory search` works as local text search even when embeddings are unavailable.
|
|
127
|
+
|
|
128
|
+
Details: [docs/context-memory-compression.md](docs/context-memory-compression.md).
|
|
129
|
+
|
|
130
|
+
## Proof
|
|
131
|
+
|
|
132
|
+
A 100-case benchmark across five task categories (coding, reasoning,
|
|
133
|
+
summarization, private, grounding), run on real backends and judged by a local
|
|
134
|
+
model, over **multiple independent runs** (means shown; full per-condition
|
|
135
|
+
numbers, confidence intervals, and significance tests are in the paper):
|
|
136
|
+
|
|
137
|
+
| Policy | Quality (1–5) | Premium usage | Privacy leaks | Answered |
|
|
138
|
+
|-------------------|:-------------:|:-------------:|:-------------:|:--------:|
|
|
139
|
+
| always-local | 3.4 | 0% | **0** | 100% |
|
|
140
|
+
| rules | 3.8 | 27% | **0** | 100% |
|
|
141
|
+
| hybrid | 3.9 | 28% | **0** | 100% |
|
|
142
|
+
| **learned** | **4.1** | 38% | **0** | 100% |
|
|
143
|
+
| always-premium | 4.6 | 100% | **0** | 61%¹ |
|
|
144
|
+
|
|
145
|
+
<sub>¹ The "just use the premium agent for everything" baseline must <em>block</em> every
|
|
146
|
+
sensitive prompt to stay leak-free, so its coverage collapses — exactly the gap
|
|
147
|
+
Switchboard closes. <strong>Zero measured leaks in every condition and every run.</strong></sub>
|
|
148
|
+
|
|
149
|
+
These numbers come from a real-backend benchmark whose full harness travels with the paper's [reproduction bundle on Zenodo](https://doi.org/10.5281/zenodo.20789935).
|
|
150
|
+
|
|
151
|
+
## Privacy
|
|
152
|
+
|
|
153
|
+
Switchboard is local-first and privacy-aware by construction:
|
|
154
|
+
|
|
155
|
+
- The **deterministic privacy floor runs before any non-local routing**; a positive verdict is final and cannot be overridden by a learned component or by prompt wording.
|
|
156
|
+
- **Secret-format detection** (cloud keys, JWTs, PEM blocks, env credentials) shares its patterns with context redaction, so the routing boundary and the redactor can't drift apart.
|
|
157
|
+
- **Metadata-only telemetry** — prompt and response bodies are not stored by default.
|
|
158
|
+
- Semantic-memory **embeddings and the eval judge run locally**.
|
|
159
|
+
|
|
160
|
+
Switchboard deliberately does **not** resell API access, scrape web UIs, or
|
|
161
|
+
bypass provider limits — subscription CLIs are invoked exactly as the
|
|
162
|
+
authenticated user could invoke them, in read-only sandbox modes. See
|
|
163
|
+
[SECURITY.md](SECURITY.md) and [docs/privacy.md](docs/privacy.md).
|
|
164
|
+
|
|
165
|
+
<details>
|
|
166
|
+
<summary><b>What's inside</b></summary>
|
|
167
|
+
|
|
168
|
+
- **Deterministic router** — keyword rules; unknown prompts default local-first.
|
|
169
|
+
- **Learned router / tool dispatcher / sensitivity escalator** — tiny softmax classifiers over a locally-computed embedding (~50 ms, pure-Python inference), each retrainable in seconds from your own thumbs-down corrections behind golden-accuracy gates. They fail closed to the deterministic path.
|
|
170
|
+
- **Tools** — time/date with timezones, safe abstract-syntax-tree calculator, unit conversion, keyless live stock quotes & news.
|
|
171
|
+
- **Compression** — structure-aware, deterministic, dependency-free; preserves task header, code blocks, tracebacks, and grounded facts.
|
|
172
|
+
- **Semantic memory** — `nomic-embed-text` embeddings, cosine retrieval, local memory commands, and SQLite text-search fallback for direct search.
|
|
173
|
+
- **Evaluation** — mock evals (CI), real-backend smoke suite, 100-case quality benchmark, adversarial tester/developer dogfooding loop.
|
|
174
|
+
|
|
175
|
+
</details>
|
|
176
|
+
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
Settings live in `config/personal.yaml` (ships with safe local-first defaults —
|
|
180
|
+
see `config/personal.example.yaml`). Highlights:
|
|
181
|
+
|
|
182
|
+
```yaml
|
|
183
|
+
preferences:
|
|
184
|
+
router_mode: "learned" # rules | llm | hybrid | learned
|
|
185
|
+
private_mode: true # block sensitive prompts from non-local backends
|
|
186
|
+
allow_cloud: false
|
|
187
|
+
compression_enabled: true
|
|
188
|
+
compression_threshold_tokens: 1000
|
|
189
|
+
semantic_memory_enabled: true
|
|
190
|
+
semantic_memory_top_k: 3
|
|
191
|
+
finance_provider: "yahoo"
|
|
192
|
+
news_provider: "google_news_rss"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Provider API keys are referenced **by environment-variable name** (e.g.
|
|
196
|
+
`OPENAI_API_KEY`), never inline. See [docs/overrides.md](docs/overrides.md).
|
|
197
|
+
|
|
198
|
+
## The paper
|
|
199
|
+
|
|
200
|
+
Switchboard is described in a preprint — *"Privacy-Aware Hybrid Routing Across
|
|
201
|
+
Heterogeneous AI Agents on a Single Workstation."* The manuscript, the multi-run
|
|
202
|
+
benchmark harness, the statistical-aggregation and figure scripts, and the
|
|
203
|
+
per-case records are archived together as a reproduction bundle on Zenodo:
|
|
204
|
+
[10.5281/zenodo.20789935](https://doi.org/10.5281/zenodo.20789935).
|
|
205
|
+
|
|
206
|
+
This repository ships only the software. It deliberately does not carry the
|
|
207
|
+
paper's experiment-running or figure-generation tooling — that lives with the
|
|
208
|
+
archival record so the code stays focused on the router itself.
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
make install # .venv + editable install with dev extras
|
|
214
|
+
make check # ruff + mypy + the full test suite
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Issues and PRs welcome — please preserve
|
|
218
|
+
the privacy invariant described there.
|
|
219
|
+
|
|
220
|
+
## Citing Switchboard
|
|
221
|
+
|
|
222
|
+
A preprint is available on Zenodo with a citable DOI —
|
|
223
|
+
[10.5281/zenodo.20789935](https://doi.org/10.5281/zenodo.20789935). See
|
|
224
|
+
[CITATION.cff](CITATION.cff) for machine-readable metadata.
|
|
225
|
+
|
|
226
|
+
> V. Gupta, "Switchboard: Privacy-Aware Hybrid Routing Across Heterogeneous AI
|
|
227
|
+
> Agents on a Single Workstation," Zenodo, 2026, doi:10.5281/zenodo.20789935.
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
[MIT](LICENSE) © 2026 Vinay Gupta
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "switchboard-local"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Privacy-aware, local-first router across CLI coding agents (Codex, Claude Code) and local LLMs (Ollama)."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Vinay Gupta", email = "ai.vinaygupta@gmail.com" }]
|
|
10
|
+
keywords = [
|
|
11
|
+
"llm", "llm-routing", "local-first", "privacy", "ollama",
|
|
12
|
+
"claude-code", "codex", "ai-agents", "orchestration", "on-device",
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"fastapi>=0.115.0",
|
|
27
|
+
"httpx>=0.27.0",
|
|
28
|
+
"pydantic>=2.8.0",
|
|
29
|
+
"pydantic-settings>=2.4.0",
|
|
30
|
+
"pyyaml>=6.0.2",
|
|
31
|
+
"sqlmodel>=0.0.22",
|
|
32
|
+
"uvicorn[standard]>=0.30.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/aivinay/switchboard"
|
|
37
|
+
Repository = "https://github.com/aivinay/switchboard"
|
|
38
|
+
Issues = "https://github.com/aivinay/switchboard/issues"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
switchboard = "switchboard.cli:main"
|
|
42
|
+
ai-switchboard = "switchboard.cli:main"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["setuptools>=69", "wheel"]
|
|
46
|
+
build-backend = "setuptools.build_meta"
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
include = ["switchboard*"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.package-data]
|
|
52
|
+
"switchboard.config" = ["*.yaml", "*.json"]
|
|
53
|
+
|
|
54
|
+
[project.optional-dependencies]
|
|
55
|
+
dev = [
|
|
56
|
+
"mypy>=1.11.0",
|
|
57
|
+
"pytest>=8.3.0",
|
|
58
|
+
"ruff>=0.6.0",
|
|
59
|
+
]
|
|
60
|
+
finance = [
|
|
61
|
+
"yfinance>=0.2.50",
|
|
62
|
+
]
|
|
63
|
+
router = [
|
|
64
|
+
"numpy>=1.26",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
69
|
+
pythonpath = ["."]
|
|
70
|
+
|
|
71
|
+
[tool.ruff]
|
|
72
|
+
line-length = 100
|
|
73
|
+
target-version = "py311"
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
python_version = "3.11"
|
|
80
|
+
plugins = ["pydantic.mypy"]
|
|
81
|
+
warn_unused_ignores = true
|
|
82
|
+
warn_redundant_casts = true
|
|
83
|
+
check_untyped_defs = true
|
|
84
|
+
disallow_untyped_defs = false
|
|
85
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FastAPI application package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""API routers."""
|