crprotocol 2.0.0__py3-none-any.whl
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.
- crp/__init__.py +126 -0
- crp/__main__.py +8 -0
- crp/_typing.py +27 -0
- crp/_version.py +5 -0
- crp/adapters.py +31 -0
- crp/advanced/__init__.py +40 -0
- crp/advanced/auto_ingest.py +400 -0
- crp/advanced/cqs.py +235 -0
- crp/advanced/cross_window.py +477 -0
- crp/advanced/curator.py +265 -0
- crp/advanced/feedback.py +146 -0
- crp/advanced/hierarchical.py +211 -0
- crp/advanced/meta_learning.py +401 -0
- crp/advanced/parallel.py +98 -0
- crp/advanced/review_cycle.py +329 -0
- crp/advanced/scale_mode.py +129 -0
- crp/advanced/source_grounding.py +207 -0
- crp/ckf/__init__.py +35 -0
- crp/ckf/community.py +377 -0
- crp/ckf/fabric.py +445 -0
- crp/ckf/gc.py +175 -0
- crp/ckf/graph_walk.py +87 -0
- crp/ckf/merge.py +133 -0
- crp/ckf/pattern_query.py +122 -0
- crp/ckf/pubsub.py +128 -0
- crp/ckf/semantic.py +207 -0
- crp/cli/__init__.py +7 -0
- crp/cli/main.py +329 -0
- crp/cli/sidecar.py +929 -0
- crp/cli/startup.py +272 -0
- crp/continuation/__init__.py +103 -0
- crp/continuation/completion.py +348 -0
- crp/continuation/degradation.py +157 -0
- crp/continuation/document_map.py +160 -0
- crp/continuation/flow.py +109 -0
- crp/continuation/gap.py +419 -0
- crp/continuation/manager.py +484 -0
- crp/continuation/quality_monitor.py +179 -0
- crp/continuation/stitch.py +419 -0
- crp/continuation/trigger.py +142 -0
- crp/continuation/voice.py +157 -0
- crp/core/__init__.py +69 -0
- crp/core/batch.py +77 -0
- crp/core/circuit_breaker.py +116 -0
- crp/core/config.py +377 -0
- crp/core/context_tools.py +540 -0
- crp/core/dispatch_router.py +3977 -0
- crp/core/errors.py +128 -0
- crp/core/extraction_facade.py +384 -0
- crp/core/facilitator.py +713 -0
- crp/core/idempotency.py +215 -0
- crp/core/orchestrator.py +1435 -0
- crp/core/relay_strategies.py +613 -0
- crp/core/security_manager.py +140 -0
- crp/core/session.py +134 -0
- crp/core/task_intent.py +36 -0
- crp/core/window.py +363 -0
- crp/envelope/__init__.py +30 -0
- crp/envelope/builder.py +288 -0
- crp/envelope/decomposer.py +236 -0
- crp/envelope/formatter.py +168 -0
- crp/envelope/packer.py +211 -0
- crp/envelope/reranker.py +209 -0
- crp/envelope/scoring.py +310 -0
- crp/extraction/__init__.py +45 -0
- crp/extraction/complexity.py +96 -0
- crp/extraction/contradiction.py +132 -0
- crp/extraction/pipeline.py +360 -0
- crp/extraction/quality_gate.py +237 -0
- crp/extraction/stage1_regex.py +173 -0
- crp/extraction/stage2_statistical.py +244 -0
- crp/extraction/stage3_gliner.py +210 -0
- crp/extraction/stage4_uie.py +183 -0
- crp/extraction/stage5_discourse.py +175 -0
- crp/extraction/stage6_llm.py +178 -0
- crp/extraction/structured_output.py +219 -0
- crp/extraction/types.py +299 -0
- crp/license_guard.py +722 -0
- crp/observability/__init__.py +30 -0
- crp/observability/audit.py +118 -0
- crp/observability/events.py +233 -0
- crp/observability/metrics.py +264 -0
- crp/observability/quality.py +135 -0
- crp/observability/structured_logging.py +81 -0
- crp/observability/telemetry.py +117 -0
- crp/provenance/__init__.py +314 -0
- crp/provenance/_embeddings.py +97 -0
- crp/provenance/_types.py +378 -0
- crp/provenance/attribution_scorer.py +252 -0
- crp/provenance/claim_detector.py +229 -0
- crp/provenance/contradiction_detector.py +243 -0
- crp/provenance/distortion_detector.py +397 -0
- crp/provenance/entailment_verifier.py +358 -0
- crp/provenance/fabrication_detector.py +203 -0
- crp/provenance/hallucination_scorer.py +320 -0
- crp/provenance/omission_analyzer.py +106 -0
- crp/provenance/provenance_chain.py +205 -0
- crp/provenance/report_generator.py +440 -0
- crp/providers/__init__.py +43 -0
- crp/providers/anthropic.py +270 -0
- crp/providers/base.py +135 -0
- crp/providers/custom.py +63 -0
- crp/providers/diagnostic.py +251 -0
- crp/providers/llamacpp.py +224 -0
- crp/providers/manager.py +139 -0
- crp/providers/ollama.py +243 -0
- crp/providers/openai.py +628 -0
- crp/providers/tokenizers.py +48 -0
- crp/py.typed +0 -0
- crp/resources/__init__.py +53 -0
- crp/resources/adaptive_allocator.py +525 -0
- crp/resources/cost_model.py +388 -0
- crp/resources/overhead_manager.py +217 -0
- crp/resources/resource_manager.py +262 -0
- crp/schemas/__init__.py +20 -0
- crp/schemas/cost-estimate.json +33 -0
- crp/schemas/crp-error.json +43 -0
- crp/schemas/envelope-preview.json +40 -0
- crp/schemas/persisted-state-header.json +27 -0
- crp/schemas/quality-report.json +94 -0
- crp/schemas/session-handle.json +33 -0
- crp/schemas/session-status.json +57 -0
- crp/schemas/stream-event.json +18 -0
- crp/schemas/task-intent.json +42 -0
- crp/security/__init__.py +93 -0
- crp/security/audit_trail.py +392 -0
- crp/security/binding.py +192 -0
- crp/security/compliance.py +813 -0
- crp/security/consent.py +593 -0
- crp/security/embedding_defense.py +161 -0
- crp/security/encryption.py +202 -0
- crp/security/injection.py +335 -0
- crp/security/integrity.py +267 -0
- crp/security/privacy.py +662 -0
- crp/security/quarantine.py +249 -0
- crp/security/rbac.py +221 -0
- crp/security/validation.py +164 -0
- crp/state/__init__.py +31 -0
- crp/state/cold_storage.py +258 -0
- crp/state/compaction.py +263 -0
- crp/state/critical_state.py +104 -0
- crp/state/event_log.py +313 -0
- crp/state/fact.py +189 -0
- crp/state/serialization.py +189 -0
- crp/state/session_cleanup.py +77 -0
- crp/state/snapshot.py +290 -0
- crp/state/warm_store.py +346 -0
- crprotocol-2.0.0.dist-info/METADATA +1295 -0
- crprotocol-2.0.0.dist-info/RECORD +153 -0
- crprotocol-2.0.0.dist-info/WHEEL +4 -0
- crprotocol-2.0.0.dist-info/entry_points.txt +2 -0
- crprotocol-2.0.0.dist-info/licenses/LICENSE.md +170 -0
- crprotocol-2.0.0.dist-info/licenses/NOTICE +18 -0
crp/cli/main.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Copyright © 2025 Constantinos Vidiniotis. All rights reserved.
|
|
2
|
+
# Licensed under Elastic License 2.0 — see LICENSE.md for details.
|
|
3
|
+
"""CRP CLI entry point — ``crp init | dispatch | ingest | status | preview | serve``.
|
|
4
|
+
|
|
5
|
+
UNIX-friendly: reads stdin, writes JSON to stdout, pipe-compatible.
|
|
6
|
+
Requires the ``click`` package (``pip install crprotocol[cli]``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
import uuid
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _require_click():
|
|
18
|
+
"""Import click with a friendly error if missing."""
|
|
19
|
+
try:
|
|
20
|
+
import click
|
|
21
|
+
return click
|
|
22
|
+
except ImportError as exc:
|
|
23
|
+
raise SystemExit(
|
|
24
|
+
"The CRP CLI requires 'click'. Install with: pip install crprotocol[cli]"
|
|
25
|
+
) from exc
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ── Session store (in-memory for CLI lifetime) ──────────────────────────
|
|
29
|
+
|
|
30
|
+
_sessions: dict[str, dict] = {}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_session(session_id: str) -> dict:
|
|
34
|
+
"""Retrieve a live session or exit with error."""
|
|
35
|
+
if session_id not in _sessions:
|
|
36
|
+
sys.stderr.write(f"Error: unknown session '{session_id}'\n")
|
|
37
|
+
raise SystemExit(1)
|
|
38
|
+
return _sessions[session_id]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ── Click application ───────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
def cli() -> None:
|
|
44
|
+
"""CRP CLI entry point — delegates to click group."""
|
|
45
|
+
# Fix Windows cp1252 encoding — ensure UTF-8 for Unicode output
|
|
46
|
+
if sys.platform == "win32":
|
|
47
|
+
import io
|
|
48
|
+
for stream_name in ("stdout", "stderr"):
|
|
49
|
+
stream = getattr(sys, stream_name)
|
|
50
|
+
if hasattr(stream, "buffer"):
|
|
51
|
+
wrapped = io.TextIOWrapper(
|
|
52
|
+
stream.buffer, encoding="utf-8", errors="replace",
|
|
53
|
+
)
|
|
54
|
+
wrapped.reconfigure(line_buffering=True)
|
|
55
|
+
setattr(sys, stream_name, wrapped)
|
|
56
|
+
|
|
57
|
+
click = _require_click()
|
|
58
|
+
|
|
59
|
+
# Build the Click group lazily so the module can be imported
|
|
60
|
+
# without click being installed (for embedded library use).
|
|
61
|
+
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
62
|
+
@click.version_option(package_name="crp")
|
|
63
|
+
def _cli():
|
|
64
|
+
"""CRP — Context Relay Protocol CLI."""
|
|
65
|
+
|
|
66
|
+
# ── crp init ────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
@_cli.command()
|
|
69
|
+
@click.option("--model", default="custom", help="Model name for diagnostics.")
|
|
70
|
+
@click.option("--context-window", default=128_000, type=int,
|
|
71
|
+
help="Context window size in tokens.")
|
|
72
|
+
@click.option("--max-output", default=4096, type=int,
|
|
73
|
+
help="Max output tokens per generation.")
|
|
74
|
+
@click.option("--timeout", default=86400, type=int,
|
|
75
|
+
help="Session timeout in seconds.")
|
|
76
|
+
@click.option("--json", "as_json", is_flag=True, help="Output raw JSON.")
|
|
77
|
+
def init(model, context_window, max_output, timeout, as_json):
|
|
78
|
+
"""Create a new CRP session and print the session ID."""
|
|
79
|
+
from crp.cli.startup import run_startup
|
|
80
|
+
from crp.providers.custom import CustomProvider
|
|
81
|
+
|
|
82
|
+
# Build a stub provider — real LLM endpoint comes via dispatch
|
|
83
|
+
provider = CustomProvider(
|
|
84
|
+
generate_fn=lambda msgs, **kw: ("", "stop"),
|
|
85
|
+
count_tokens_fn=lambda t: max(1, len(t) // 4),
|
|
86
|
+
context_size=context_window,
|
|
87
|
+
name=model,
|
|
88
|
+
max_output=max_output,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
result, ctx = run_startup(
|
|
92
|
+
provider=provider,
|
|
93
|
+
config_overrides={"session_timeout": timeout},
|
|
94
|
+
skip_health=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
session_id = str(uuid.uuid4())
|
|
98
|
+
_sessions[session_id] = {
|
|
99
|
+
"id": session_id,
|
|
100
|
+
"provider": provider,
|
|
101
|
+
"config": ctx.get("config"),
|
|
102
|
+
"emitter": ctx.get("emitter"),
|
|
103
|
+
"orchestrator": None, # lazy
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if as_json:
|
|
107
|
+
click.echo(json.dumps({
|
|
108
|
+
"session_id": session_id,
|
|
109
|
+
"model": model,
|
|
110
|
+
"context_window": context_window,
|
|
111
|
+
"startup_ok": result.ok,
|
|
112
|
+
"startup_ms": round(result.total_ms, 2),
|
|
113
|
+
}))
|
|
114
|
+
else:
|
|
115
|
+
click.echo(session_id)
|
|
116
|
+
|
|
117
|
+
# ── crp dispatch ────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
@_cli.command()
|
|
120
|
+
@click.option("--session", "session_id", default=None, help="Session ID (auto-creates if omitted).")
|
|
121
|
+
@click.option("--task", required=True, help="Task input text.")
|
|
122
|
+
@click.option("--system", default="You are a helpful assistant.",
|
|
123
|
+
help="System prompt.")
|
|
124
|
+
@click.option("--json", "as_json", is_flag=True, help="Output JSON report.")
|
|
125
|
+
def dispatch(session_id, task, system, as_json):
|
|
126
|
+
"""Dispatch a task to the LLM via CRP and print the output.
|
|
127
|
+
|
|
128
|
+
If --session is omitted a one-shot session is created automatically
|
|
129
|
+
so that ``crp dispatch --task "Hello"`` works without ``crp init``.
|
|
130
|
+
"""
|
|
131
|
+
if session_id is None:
|
|
132
|
+
# Auto-init a one-shot session
|
|
133
|
+
from crp.cli.startup import run_startup
|
|
134
|
+
from crp.providers.custom import CustomProvider
|
|
135
|
+
|
|
136
|
+
provider = CustomProvider(
|
|
137
|
+
generate_fn=lambda msgs, **kw: ("", "stop"),
|
|
138
|
+
count_tokens_fn=lambda t: max(1, len(t) // 4),
|
|
139
|
+
context_size=128_000,
|
|
140
|
+
)
|
|
141
|
+
result, ctx = run_startup(
|
|
142
|
+
provider=provider, skip_health=True,
|
|
143
|
+
)
|
|
144
|
+
session_id = str(uuid.uuid4())
|
|
145
|
+
_sessions[session_id] = {
|
|
146
|
+
"id": session_id,
|
|
147
|
+
"provider": provider,
|
|
148
|
+
"config": ctx.get("config"),
|
|
149
|
+
"emitter": ctx.get("emitter"),
|
|
150
|
+
"orchestrator": None,
|
|
151
|
+
}
|
|
152
|
+
sess = _get_session(session_id)
|
|
153
|
+
orch = _ensure_orchestrator(sess)
|
|
154
|
+
|
|
155
|
+
output, report = orch.dispatch(system, task)
|
|
156
|
+
|
|
157
|
+
if as_json:
|
|
158
|
+
click.echo(json.dumps({
|
|
159
|
+
"session_id": report.session_id,
|
|
160
|
+
"window_id": report.window_id,
|
|
161
|
+
"output": output,
|
|
162
|
+
"facts_extracted": report.facts_extracted,
|
|
163
|
+
}))
|
|
164
|
+
else:
|
|
165
|
+
click.echo(output)
|
|
166
|
+
|
|
167
|
+
# ── crp ingest ──────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
@_cli.command()
|
|
170
|
+
@click.option("--session", "session_id", required=True, help="Session ID.")
|
|
171
|
+
@click.option("--label", default="stdin", help="Source label.")
|
|
172
|
+
@click.option("--json", "as_json", is_flag=True, help="Output JSON.")
|
|
173
|
+
@click.argument("file", default="-", type=click.File("r"))
|
|
174
|
+
def ingest(session_id, label, as_json, file):
|
|
175
|
+
"""Ingest raw text (file or stdin) into the session."""
|
|
176
|
+
sess = _get_session(session_id)
|
|
177
|
+
orch = _ensure_orchestrator(sess)
|
|
178
|
+
text = file.read()
|
|
179
|
+
result = orch.ingest(text, source_label=label)
|
|
180
|
+
|
|
181
|
+
if as_json:
|
|
182
|
+
click.echo(json.dumps({
|
|
183
|
+
"facts_extracted": result.facts_extracted,
|
|
184
|
+
"source_label": result.source_label,
|
|
185
|
+
"fact_ids": result.fact_ids,
|
|
186
|
+
}))
|
|
187
|
+
else:
|
|
188
|
+
click.echo(f"Ingested {result.facts_extracted} facts from '{label}'")
|
|
189
|
+
|
|
190
|
+
# ── crp status ──────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
@_cli.command()
|
|
193
|
+
@click.option("--session", "session_id", required=True, help="Session ID.")
|
|
194
|
+
def status(session_id):
|
|
195
|
+
"""Print session status as JSON."""
|
|
196
|
+
sess = _get_session(session_id)
|
|
197
|
+
orch = _ensure_orchestrator(sess)
|
|
198
|
+
st = orch.session_status()
|
|
199
|
+
click.echo(json.dumps({
|
|
200
|
+
"session_id": st.session_id,
|
|
201
|
+
"windows_completed": st.windows_completed,
|
|
202
|
+
"total_input_tokens": st.total_input_tokens,
|
|
203
|
+
"total_output_tokens": st.total_output_tokens,
|
|
204
|
+
"facts_in_warm_state": st.facts_in_warm_state,
|
|
205
|
+
"overhead_ratio": st.overhead_ratio,
|
|
206
|
+
}, indent=2))
|
|
207
|
+
|
|
208
|
+
# ── crp preview ─────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
@_cli.command()
|
|
211
|
+
@click.option("--session", "session_id", required=True, help="Session ID.")
|
|
212
|
+
@click.option("--task", required=True, help="Task input text.")
|
|
213
|
+
@click.option("--system", default="You are a helpful assistant.",
|
|
214
|
+
help="System prompt.")
|
|
215
|
+
def preview(session_id, task, system):
|
|
216
|
+
"""Preview envelope without dispatching — prints JSON."""
|
|
217
|
+
sess = _get_session(session_id)
|
|
218
|
+
orch = _ensure_orchestrator(sess)
|
|
219
|
+
ep = orch.preview_envelope(system, task)
|
|
220
|
+
click.echo(json.dumps({
|
|
221
|
+
"total_tokens": ep.total_tokens,
|
|
222
|
+
"envelope_tokens": ep.envelope_tokens,
|
|
223
|
+
"generation_reserve": ep.generation_reserve,
|
|
224
|
+
"facts_included": ep.facts_included,
|
|
225
|
+
"facts_available": ep.facts_available,
|
|
226
|
+
"saturation": round(ep.saturation, 4),
|
|
227
|
+
}, indent=2))
|
|
228
|
+
|
|
229
|
+
# ── crp serve (HTTP sidecar for inter-LLM context sharing) ────
|
|
230
|
+
|
|
231
|
+
@_cli.command()
|
|
232
|
+
@click.option("--port", default=9470, type=int, help="Port number.")
|
|
233
|
+
@click.option("--bind-all", is_flag=True, default=False,
|
|
234
|
+
help="Bind to 0.0.0.0 instead of 127.0.0.1. Requires --auth-token unless --allow-unauthenticated is set.")
|
|
235
|
+
@click.option("--auth-token", default=None, help="Bearer token for authentication (strongly recommended).")
|
|
236
|
+
@click.option("--allow-unauthenticated", is_flag=True, default=False,
|
|
237
|
+
help="Allow running without auth token (NOT recommended for --bind-all).")
|
|
238
|
+
@click.option("--max-sessions", default=64, type=int, help="Maximum concurrent sessions.")
|
|
239
|
+
@click.option("--rate-limit", default=120, type=int, help="Max requests per IP per rate window.")
|
|
240
|
+
def serve(port, bind_all, auth_token, allow_unauthenticated, max_sessions, rate_limit):
|
|
241
|
+
"""Start the CRP HTTP sidecar for inter-LLM context sharing.
|
|
242
|
+
|
|
243
|
+
The sidecar is OPTIONAL — it is never started automatically.
|
|
244
|
+
It exposes CRP sessions over HTTP, enabling multiple applications
|
|
245
|
+
and LLMs to share extracted knowledge.
|
|
246
|
+
|
|
247
|
+
\b
|
|
248
|
+
SECURITY:
|
|
249
|
+
- Binds to 127.0.0.1 (loopback) by default
|
|
250
|
+
- --auth-token enables bearer-token authentication
|
|
251
|
+
- --bind-all requires --auth-token (or explicit --allow-unauthenticated)
|
|
252
|
+
- Per-IP rate limiting (default: 120 req/60s)
|
|
253
|
+
- Session ownership: only the token that created a session can access it
|
|
254
|
+
- 10 MB request body limit
|
|
255
|
+
- No HTTPS — deploy behind a TLS reverse proxy for production
|
|
256
|
+
|
|
257
|
+
\b
|
|
258
|
+
ENDPOINTS (all 6 dispatch variants + knowledge sharing):
|
|
259
|
+
POST /sessions Create session
|
|
260
|
+
POST /sessions/:id/dispatch Basic dispatch
|
|
261
|
+
POST /sessions/:id/dispatch/tools Tool-mediated dispatch
|
|
262
|
+
POST /sessions/:id/dispatch/reflexive Reflexive dispatch
|
|
263
|
+
POST /sessions/:id/dispatch/progressive Progressive dispatch
|
|
264
|
+
POST /sessions/:id/dispatch/stream-augmented Stream-augmented
|
|
265
|
+
POST /sessions/:id/dispatch/agentic Agentic dispatch
|
|
266
|
+
POST /sessions/:id/ingest Ingest text
|
|
267
|
+
GET /sessions/:id/facts Query facts
|
|
268
|
+
POST /sessions/:id/facts/share SHARE facts between sessions
|
|
269
|
+
POST /sessions/:id/facts/feedback Boost/penalize/reject facts
|
|
270
|
+
POST /sessions/:id/providers Register fallback provider
|
|
271
|
+
POST /sessions/:id/estimate Cost estimation
|
|
272
|
+
GET /sessions/:id/status Session status
|
|
273
|
+
GET /sessions/:id/envelope Envelope preview
|
|
274
|
+
POST /sessions/:id/close Close session
|
|
275
|
+
GET /sessions List sessions (owned only)
|
|
276
|
+
GET /health Health check
|
|
277
|
+
|
|
278
|
+
The /facts/share endpoint is the core differentiator — it enables
|
|
279
|
+
two LLMs using CRP to share each other's extracted knowledge.
|
|
280
|
+
"""
|
|
281
|
+
from crp.cli.sidecar import start_sidecar
|
|
282
|
+
|
|
283
|
+
host = "0.0.0.0" if bind_all else "127.0.0.1"
|
|
284
|
+
|
|
285
|
+
# Security gate: --bind-all requires auth unless explicitly waived
|
|
286
|
+
if bind_all and not auth_token and not allow_unauthenticated:
|
|
287
|
+
click.echo("ERROR: --bind-all requires --auth-token for security.", err=True)
|
|
288
|
+
click.echo(" Use --allow-unauthenticated to override (NOT recommended).", err=True)
|
|
289
|
+
raise SystemExit(1)
|
|
290
|
+
|
|
291
|
+
if bind_all and not auth_token:
|
|
292
|
+
click.echo("WARNING: Binding to 0.0.0.0 WITHOUT authentication!", err=True)
|
|
293
|
+
click.echo(" Any process on the network can access CRP sessions.", err=True)
|
|
294
|
+
|
|
295
|
+
click.echo(f"CRP sidecar starting on {host}:{port}")
|
|
296
|
+
click.echo(f" Auth: {'Bearer token required' if auth_token else 'DISABLED'}")
|
|
297
|
+
click.echo(f" Max sessions: {max_sessions}")
|
|
298
|
+
click.echo(f" Rate limit: {rate_limit} req/60s per IP")
|
|
299
|
+
click.echo()
|
|
300
|
+
click.echo(f" Dispatch variants: basic, tools, reflexive, progressive, stream-augmented, agentic")
|
|
301
|
+
click.echo(f" POST /sessions/:id/facts/share ← Inter-LLM context sharing")
|
|
302
|
+
click.echo(f" POST /sessions/:id/facts/feedback ← Fact confidence adjustment")
|
|
303
|
+
click.echo(f" GET /health")
|
|
304
|
+
click.echo()
|
|
305
|
+
|
|
306
|
+
server = start_sidecar(host=host, port=port, auth_token=auth_token,
|
|
307
|
+
max_sessions=max_sessions, rate_limit=rate_limit)
|
|
308
|
+
try:
|
|
309
|
+
server.serve_forever()
|
|
310
|
+
except KeyboardInterrupt:
|
|
311
|
+
click.echo("\nShutting down sidecar...")
|
|
312
|
+
server.shutdown()
|
|
313
|
+
|
|
314
|
+
_cli(standalone_mode=True)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# ── Helpers ─────────────────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
def _ensure_orchestrator(sess: dict) -> Any: # type: ignore[type-arg]
|
|
320
|
+
"""Lazily build the orchestrator for this session."""
|
|
321
|
+
if sess["orchestrator"] is None:
|
|
322
|
+
from crp.core.orchestrator import CRPOrchestrator
|
|
323
|
+
|
|
324
|
+
orch = CRPOrchestrator(
|
|
325
|
+
provider=sess["provider"],
|
|
326
|
+
config=sess["config"],
|
|
327
|
+
)
|
|
328
|
+
sess["orchestrator"] = orch
|
|
329
|
+
return sess["orchestrator"]
|