algo-cli-runtime 0.14.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.
- algo_cli/__init__.py +3 -0
- algo_cli/__main__.py +7 -0
- algo_cli/_internal/__init__.py +12 -0
- algo_cli/_internal/policy_chain.py +259 -0
- algo_cli/action_registry.py +1047 -0
- algo_cli/agent_blocks.py +550 -0
- algo_cli/agent_pipeline.py +1457 -0
- algo_cli/agent_threads.py +308 -0
- algo_cli/animations.py +316 -0
- algo_cli/cache_admission.py +209 -0
- algo_cli/capability_mask.py +66 -0
- algo_cli/chat_protocol.py +116 -0
- algo_cli/chatgpt_auth.py +510 -0
- algo_cli/chatgpt_client.py +657 -0
- algo_cli/code_rag.py +479 -0
- algo_cli/config.py +651 -0
- algo_cli/context_budget.py +679 -0
- algo_cli/credential_helpers.py +315 -0
- algo_cli/deliberation.py +29 -0
- algo_cli/display.py +1470 -0
- algo_cli/evals/__init__.py +21 -0
- algo_cli/evals/algorithm_effectiveness.py +560 -0
- algo_cli/evals/competitive_harness_rating.py +702 -0
- algo_cli/evals/cot_quality.py +220 -0
- algo_cli/evals/harness_retrieval_benchmark.py +401 -0
- algo_cli/evals/performance_regression.py +136 -0
- algo_cli/evals/scorecard_grading.py +308 -0
- algo_cli/evals/session_distribution.py +84 -0
- algo_cli/execution_guardrails.py +806 -0
- algo_cli/extensions_manifest.py +84 -0
- algo_cli/git_evidence.py +227 -0
- algo_cli/google_workspace.py +407 -0
- algo_cli/google_workspace_auth.py +523 -0
- algo_cli/harness.py +2587 -0
- algo_cli/identity.py +557 -0
- algo_cli/index_compute_lab.py +228 -0
- algo_cli/inference_harness.py +70 -0
- algo_cli/intelligence/__init__.py +1103 -0
- algo_cli/intelligence/acrobat_config.py +307 -0
- algo_cli/intelligence/acrobat_manifests.py +338 -0
- algo_cli/intelligence/acrobat_models.py +195 -0
- algo_cli/intelligence/acrobat_pipeline.py +295 -0
- algo_cli/intelligence/acrobat_runtime.py +302 -0
- algo_cli/intelligence/acrobat_security.py +261 -0
- algo_cli/intelligence/acrobat_workflows.py +226 -0
- algo_cli/intelligence/actionability.py +165 -0
- algo_cli/intelligence/adversarial_audit.py +136 -0
- algo_cli/intelligence/agent_arena.py +92 -0
- algo_cli/intelligence/agent_benchmark.py +236 -0
- algo_cli/intelligence/agent_runtime.py +171 -0
- algo_cli/intelligence/agents_as_tools.py +70 -0
- algo_cli/intelligence/artifact_binding.py +80 -0
- algo_cli/intelligence/autonomous_engineer.py +1976 -0
- algo_cli/intelligence/backpressure.py +99 -0
- algo_cli/intelligence/bloom_filter.py +186 -0
- algo_cli/intelligence/bonferroni.py +66 -0
- algo_cli/intelligence/boundary_compaction.py +98 -0
- algo_cli/intelligence/catalog_verifier.py +172 -0
- algo_cli/intelligence/cavecrew.py +118 -0
- algo_cli/intelligence/changelog.py +176 -0
- algo_cli/intelligence/checkpoint_resume.py +92 -0
- algo_cli/intelligence/circuit_breaker.py +88 -0
- algo_cli/intelligence/clarification_gate.py +101 -0
- algo_cli/intelligence/code_graph.py +180 -0
- algo_cli/intelligence/coderank.py +97 -0
- algo_cli/intelligence/consistent_hash.py +150 -0
- algo_cli/intelligence/consortium_synthesis.py +139 -0
- algo_cli/intelligence/construction/__init__.py +241 -0
- algo_cli/intelligence/construction/common.py +273 -0
- algo_cli/intelligence/construction/documents.py +496 -0
- algo_cli/intelligence/construction/labor_units.py +1395 -0
- algo_cli/intelligence/construction/payments.py +470 -0
- algo_cli/intelligence/construction/risk.py +784 -0
- algo_cli/intelligence/content_extractor.py +132 -0
- algo_cli/intelligence/context_adaptive.py +102 -0
- algo_cli/intelligence/context_ops.py +95 -0
- algo_cli/intelligence/count_min.py +145 -0
- algo_cli/intelligence/cow_state.py +103 -0
- algo_cli/intelligence/critic_loop.py +119 -0
- algo_cli/intelligence/cross_source.py +113 -0
- algo_cli/intelligence/daemon_mode.py +99 -0
- algo_cli/intelligence/dag_orchestration.py +151 -0
- algo_cli/intelligence/deep_research.py +155 -0
- algo_cli/intelligence/degenerate_detector.py +78 -0
- algo_cli/intelligence/delta_report.py +92 -0
- algo_cli/intelligence/discovery_event_log.py +92 -0
- algo_cli/intelligence/document_ingest.py +298 -0
- algo_cli/intelligence/dual_layer_validate.py +151 -0
- algo_cli/intelligence/echo_fidelity.py +73 -0
- algo_cli/intelligence/ema_tuning.py +104 -0
- algo_cli/intelligence/event_log.py +92 -0
- algo_cli/intelligence/evidence_graph.py +114 -0
- algo_cli/intelligence/extension_host.py +162 -0
- algo_cli/intelligence/extension_manifest.py +115 -0
- algo_cli/intelligence/falsification_suite.py +178 -0
- algo_cli/intelligence/finance/__init__.py +169 -0
- algo_cli/intelligence/finance/anomalies.py +135 -0
- algo_cli/intelligence/finance/ap_ar.py +351 -0
- algo_cli/intelligence/finance/cash.py +162 -0
- algo_cli/intelligence/finance/close.py +332 -0
- algo_cli/intelligence/finance/common.py +244 -0
- algo_cli/intelligence/finance/construction.py +135 -0
- algo_cli/intelligence/finance/controls.py +172 -0
- algo_cli/intelligence/finance/evidence.py +119 -0
- algo_cli/intelligence/finance/exceptions.py +157 -0
- algo_cli/intelligence/finance/reconciliations.py +254 -0
- algo_cli/intelligence/finance/revenue.py +109 -0
- algo_cli/intelligence/finance/tax.py +74 -0
- algo_cli/intelligence/finance/workpapers.py +111 -0
- algo_cli/intelligence/finding_record.py +120 -0
- algo_cli/intelligence/flow_dag.py +267 -0
- algo_cli/intelligence/gatherer.py +223 -0
- algo_cli/intelligence/golden_master.py +98 -0
- algo_cli/intelligence/graph_rag.py +195 -0
- algo_cli/intelligence/group_chat.py +143 -0
- algo_cli/intelligence/hash_dedup.py +145 -0
- algo_cli/intelligence/hyperloglog.py +128 -0
- algo_cli/intelligence/incremental_index.py +316 -0
- algo_cli/intelligence/index_store.py +16 -0
- algo_cli/intelligence/iteration_plan.py +133 -0
- algo_cli/intelligence/kernel_plugins.py +167 -0
- algo_cli/intelligence/lesson_catalog.py +135 -0
- algo_cli/intelligence/llm_fallback.py +169 -0
- algo_cli/intelligence/log2_histogram.py +267 -0
- algo_cli/intelligence/lsp_integration.py +147 -0
- algo_cli/intelligence/memory_evolution.py +117 -0
- algo_cli/intelligence/minhash_lsh.py +182 -0
- algo_cli/intelligence/multi_model_score.py +174 -0
- algo_cli/intelligence/multi_tier_grade.py +211 -0
- algo_cli/intelligence/negative_controls.py +113 -0
- algo_cli/intelligence/numeric_clamp.py +63 -0
- algo_cli/intelligence/occ_editor.py +66 -0
- algo_cli/intelligence/output_normalize.py +112 -0
- algo_cli/intelligence/parallel_delegation.py +98 -0
- algo_cli/intelligence/parallel_fanout.py +104 -0
- algo_cli/intelligence/permission_modes.py +105 -0
- algo_cli/intelligence/pre_push_gate.py +68 -0
- algo_cli/intelligence/prefetch.py +171 -0
- algo_cli/intelligence/process_framework.py +217 -0
- algo_cli/intelligence/project_graph.py +387 -0
- algo_cli/intelligence/query_expansion.py +146 -0
- algo_cli/intelligence/ralph_loop.py +117 -0
- algo_cli/intelligence/rate_limiter.py +153 -0
- algo_cli/intelligence/refactor_transaction.py +94 -0
- algo_cli/intelligence/research_workspace.py +108 -0
- algo_cli/intelligence/retraction_ledger.py +72 -0
- algo_cli/intelligence/saga_pattern.py +88 -0
- algo_cli/intelligence/session_fork.py +100 -0
- algo_cli/intelligence/shadow_editor.py +67 -0
- algo_cli/intelligence/shell_session.py +213 -0
- algo_cli/intelligence/source_registry.py +143 -0
- algo_cli/intelligence/spawn_scales.py +99 -0
- algo_cli/intelligence/stat_stability.py +104 -0
- algo_cli/intelligence/structural_validator.py +148 -0
- algo_cli/intelligence/subagent_spawner.py +111 -0
- algo_cli/intelligence/symmetric_verify.py +70 -0
- algo_cli/intelligence/task_classifier.py +129 -0
- algo_cli/intelligence/team_execution.py +122 -0
- algo_cli/intelligence/tiered_access.py +121 -0
- algo_cli/intelligence/utility_registry.py +159 -0
- algo_cli/intuition_engine.py +560 -0
- algo_cli/intuition_injector.py +82 -0
- algo_cli/kernels/__init__.py +5 -0
- algo_cli/kernels/manifest.py +763 -0
- algo_cli/main.py +3903 -0
- algo_cli/memory_candidates.py +541 -0
- algo_cli/memory_echo_veil.py +394 -0
- algo_cli/memory_runtime.py +112 -0
- algo_cli/model_info.py +548 -0
- algo_cli/model_profile.py +160 -0
- algo_cli/model_routing.py +74 -0
- algo_cli/oneshot.py +331 -0
- algo_cli/perf_telemetry.py +389 -0
- algo_cli/plugins.py +245 -0
- algo_cli/private_event_store.py +654 -0
- algo_cli/quantization/__init__.py +24 -0
- algo_cli/quantization/lloyd_max.py +98 -0
- algo_cli/quantization/turbo_quant.py +308 -0
- algo_cli/reasoning/__init__.py +46 -0
- algo_cli/reasoning/combinatorial.py +356 -0
- algo_cli/reasoning/graph_of_thought.py +297 -0
- algo_cli/reasoning/mcts.py +220 -0
- algo_cli/reasoning/neuro_symbolic.py +250 -0
- algo_cli/reasoning/react.py +246 -0
- algo_cli/reasoning/reflexion.py +225 -0
- algo_cli/reasoning/tree_of_thought.py +241 -0
- algo_cli/reasoning_bridge.py +150 -0
- algo_cli/reconciliation.py +284 -0
- algo_cli/reflex.py +385 -0
- algo_cli/resources/docs/ALGO.md +13958 -0
- algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
- algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
- algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
- algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
- algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
- algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
- algo_cli/resources/docs/main-split-map.md +35 -0
- algo_cli/resources/docs/privacy-and-context.md +48 -0
- algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
- algo_cli/resources/skills/README.md +26 -0
- algo_cli/resources/skills/algo-cli.md +59 -0
- algo_cli/resources/skills/edit-file-precision.md +49 -0
- algo_cli/resources/skills/harness-search-first.md +47 -0
- algo_cli/resources/skills/memory-recall-ritual.md +51 -0
- algo_cli/resources/skills/qol-algorithms.md +224 -0
- algo_cli/resources/skills/smart-error-recovery.md +56 -0
- algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
- algo_cli/retrieval_algorithms.py +127 -0
- algo_cli/runtime_qos.py +236 -0
- algo_cli/runtime_services.py +320 -0
- algo_cli/session_commands.py +95 -0
- algo_cli/session_mode.py +113 -0
- algo_cli/skills.py +430 -0
- algo_cli/slash_dispatch.py +1265 -0
- algo_cli/small_context.py +206 -0
- algo_cli/spawn_budget.py +89 -0
- algo_cli/task_ledger.py +84 -0
- algo_cli/task_router.py +197 -0
- algo_cli/tool_context.py +94 -0
- algo_cli/tool_contract.py +99 -0
- algo_cli/tool_policy.py +357 -0
- algo_cli/tool_runtime.py +647 -0
- algo_cli/tools.py +3056 -0
- algo_cli/url_scheme.py +174 -0
- algo_cli/verify.py +154 -0
- algo_cli/version_manifest.py +178 -0
- algo_cli/vision_screenshot_verify.py +76 -0
- algo_cli/workspace_resolver.py +68 -0
- algo_cli/x_account.py +209 -0
- algo_cli/xai_auth.py +374 -0
- algo_cli/xai_client.py +600 -0
- algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
- algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
- algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
- algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
- algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
- ollama_cli/__init__.py +67 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Bounded Window TinyLFU cache admission for hot runtime data.
|
|
2
|
+
|
|
3
|
+
The cache keeps a small recency window in front of a larger main LRU segment.
|
|
4
|
+
When the window overflows, a bounded frequency sketch decides whether the
|
|
5
|
+
candidate should displace the main segment's least-recently-used entry. This
|
|
6
|
+
prevents one-off scans from evicting repeatedly used query embeddings.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import math
|
|
13
|
+
import threading
|
|
14
|
+
from collections import OrderedDict
|
|
15
|
+
from dataclasses import asdict, dataclass
|
|
16
|
+
from typing import Generic, Iterator, TypeVar
|
|
17
|
+
|
|
18
|
+
K = TypeVar("K")
|
|
19
|
+
V = TypeVar("V")
|
|
20
|
+
|
|
21
|
+
_MISSING = object()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FrequencySketch(Generic[K]):
|
|
25
|
+
"""Small count-min sketch with periodic aging and stable hashing."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, capacity: int, *, depth: int = 4, sample_multiplier: int = 10) -> None:
|
|
28
|
+
target_width = max(16, max(1, int(capacity)) * 4)
|
|
29
|
+
self.width = 1 << math.ceil(math.log2(target_width))
|
|
30
|
+
self.depth = min(4, max(2, int(depth)))
|
|
31
|
+
self.sample_size = max(10, max(1, int(capacity)) * max(2, int(sample_multiplier)))
|
|
32
|
+
self._rows = [[0] * self.width for _ in range(self.depth)]
|
|
33
|
+
self._samples = 0
|
|
34
|
+
self.decays = 0
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _digest(key: object) -> bytes:
|
|
38
|
+
return hashlib.blake2b(repr(key).encode("utf-8", errors="replace"), digest_size=16).digest()
|
|
39
|
+
|
|
40
|
+
def _indexes(self, key: K) -> Iterator[tuple[int, int]]:
|
|
41
|
+
digest = self._digest(key)
|
|
42
|
+
for row in range(self.depth):
|
|
43
|
+
offset = row * 4
|
|
44
|
+
value = int.from_bytes(digest[offset:offset + 4], "little", signed=False)
|
|
45
|
+
yield row, value & (self.width - 1)
|
|
46
|
+
|
|
47
|
+
def increment(self, key: K) -> None:
|
|
48
|
+
for row, index in self._indexes(key):
|
|
49
|
+
if self._rows[row][index] < 2_147_483_647:
|
|
50
|
+
self._rows[row][index] += 1
|
|
51
|
+
self._samples += 1
|
|
52
|
+
if self._samples >= self.sample_size:
|
|
53
|
+
self._decay()
|
|
54
|
+
|
|
55
|
+
def estimate(self, key: K) -> int:
|
|
56
|
+
return min(self._rows[row][index] for row, index in self._indexes(key))
|
|
57
|
+
|
|
58
|
+
def _decay(self) -> None:
|
|
59
|
+
for row in self._rows:
|
|
60
|
+
for index, count in enumerate(row):
|
|
61
|
+
row[index] = count // 2
|
|
62
|
+
self._samples //= 2
|
|
63
|
+
self.decays += 1
|
|
64
|
+
|
|
65
|
+
def clear(self) -> None:
|
|
66
|
+
self._rows = [[0] * self.width for _ in range(self.depth)]
|
|
67
|
+
self._samples = 0
|
|
68
|
+
self.decays = 0
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class CacheAdmissionStats:
|
|
73
|
+
hits: int = 0
|
|
74
|
+
misses: int = 0
|
|
75
|
+
admissions: int = 0
|
|
76
|
+
rejections: int = 0
|
|
77
|
+
evictions: int = 0
|
|
78
|
+
|
|
79
|
+
def to_dict(self) -> dict[str, int]:
|
|
80
|
+
return asdict(self)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class WindowTinyLFUCache(Generic[K, V]):
|
|
84
|
+
"""Thread-safe, entry-bounded Window TinyLFU cache.
|
|
85
|
+
|
|
86
|
+
This intentionally exposes only the small mapping surface used by Algo CLI:
|
|
87
|
+
``get``, ``put``/item assignment, ``clear``, membership, and length.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, capacity: int, *, window_fraction: float = 0.20) -> None:
|
|
91
|
+
self.window_fraction = min(0.50, max(0.01, float(window_fraction)))
|
|
92
|
+
self._lock = threading.RLock()
|
|
93
|
+
self.stats = CacheAdmissionStats()
|
|
94
|
+
self._set_capacity(capacity)
|
|
95
|
+
self._window: OrderedDict[K, V] = OrderedDict()
|
|
96
|
+
self._main: OrderedDict[K, V] = OrderedDict()
|
|
97
|
+
self._sketch: FrequencySketch[K] = FrequencySketch(self.capacity)
|
|
98
|
+
|
|
99
|
+
def _set_capacity(self, capacity: int) -> None:
|
|
100
|
+
self.capacity = max(1, int(capacity))
|
|
101
|
+
self.window_capacity = max(1, min(self.capacity, round(self.capacity * self.window_fraction)))
|
|
102
|
+
self.main_capacity = self.capacity - self.window_capacity
|
|
103
|
+
|
|
104
|
+
def resize(self, capacity: int) -> None:
|
|
105
|
+
target = max(1, int(capacity))
|
|
106
|
+
with self._lock:
|
|
107
|
+
if target == self.capacity:
|
|
108
|
+
return
|
|
109
|
+
retained = list(self._main.items()) + list(self._window.items())
|
|
110
|
+
self._set_capacity(target)
|
|
111
|
+
self._window.clear()
|
|
112
|
+
self._main.clear()
|
|
113
|
+
self._sketch = FrequencySketch(self.capacity)
|
|
114
|
+
for key, value in retained[-self.capacity:]:
|
|
115
|
+
self.put(key, value)
|
|
116
|
+
self.stats = CacheAdmissionStats()
|
|
117
|
+
|
|
118
|
+
def get(self, key: K, default: V | None = None) -> V | None:
|
|
119
|
+
with self._lock:
|
|
120
|
+
value: object = _MISSING
|
|
121
|
+
if key in self._window:
|
|
122
|
+
value = self._window[key]
|
|
123
|
+
self._window.move_to_end(key)
|
|
124
|
+
elif key in self._main:
|
|
125
|
+
value = self._main[key]
|
|
126
|
+
self._main.move_to_end(key)
|
|
127
|
+
self._sketch.increment(key)
|
|
128
|
+
if value is _MISSING:
|
|
129
|
+
self.stats.misses += 1
|
|
130
|
+
return default
|
|
131
|
+
self.stats.hits += 1
|
|
132
|
+
return value # type: ignore[return-value]
|
|
133
|
+
|
|
134
|
+
def put(self, key: K, value: V) -> bool:
|
|
135
|
+
"""Store a value after lookup and report overflow-candidate admission.
|
|
136
|
+
|
|
137
|
+
Lookup records the request in the frequency sketch. ``put`` deliberately
|
|
138
|
+
does not record it again, or every miss/fill would count twice and make
|
|
139
|
+
one-off scans look hotter than they are.
|
|
140
|
+
"""
|
|
141
|
+
with self._lock:
|
|
142
|
+
if key in self._window:
|
|
143
|
+
self._window[key] = value
|
|
144
|
+
self._window.move_to_end(key)
|
|
145
|
+
return True
|
|
146
|
+
if key in self._main:
|
|
147
|
+
self._main[key] = value
|
|
148
|
+
self._main.move_to_end(key)
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
self._window[key] = value
|
|
152
|
+
self.stats.admissions += 1
|
|
153
|
+
if len(self._window) <= self.window_capacity:
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
candidate_key, candidate_value = self._window.popitem(last=False)
|
|
157
|
+
if self.main_capacity <= 0:
|
|
158
|
+
self.stats.evictions += 1
|
|
159
|
+
return candidate_key != key
|
|
160
|
+
if len(self._main) < self.main_capacity:
|
|
161
|
+
self._main[candidate_key] = candidate_value
|
|
162
|
+
return True
|
|
163
|
+
|
|
164
|
+
victim_key = next(iter(self._main))
|
|
165
|
+
candidate_frequency = self._sketch.estimate(candidate_key)
|
|
166
|
+
victim_frequency = self._sketch.estimate(victim_key)
|
|
167
|
+
if candidate_frequency > victim_frequency:
|
|
168
|
+
self._main.popitem(last=False)
|
|
169
|
+
self._main[candidate_key] = candidate_value
|
|
170
|
+
self.stats.evictions += 1
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
self.stats.rejections += 1
|
|
174
|
+
self.stats.evictions += 1
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def snapshot(self) -> dict[str, int | float]:
|
|
178
|
+
with self._lock:
|
|
179
|
+
requests = self.stats.hits + self.stats.misses
|
|
180
|
+
return {
|
|
181
|
+
**self.stats.to_dict(),
|
|
182
|
+
"size": len(self),
|
|
183
|
+
"capacity": self.capacity,
|
|
184
|
+
"window_size": len(self._window),
|
|
185
|
+
"main_size": len(self._main),
|
|
186
|
+
"sketch_decays": self._sketch.decays,
|
|
187
|
+
"hit_ratio": round(self.stats.hits / requests, 4) if requests else 0.0,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
def clear(self) -> None:
|
|
191
|
+
with self._lock:
|
|
192
|
+
self._window.clear()
|
|
193
|
+
self._main.clear()
|
|
194
|
+
self._sketch.clear()
|
|
195
|
+
self.stats = CacheAdmissionStats()
|
|
196
|
+
|
|
197
|
+
def __contains__(self, key: object) -> bool:
|
|
198
|
+
with self._lock:
|
|
199
|
+
return key in self._window or key in self._main
|
|
200
|
+
|
|
201
|
+
def __len__(self) -> int:
|
|
202
|
+
with self._lock:
|
|
203
|
+
return len(self._window) + len(self._main)
|
|
204
|
+
|
|
205
|
+
def __setitem__(self, key: K, value: V) -> None:
|
|
206
|
+
self.put(key, value)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
__all__ = ["CacheAdmissionStats", "FrequencySketch", "WindowTinyLFUCache"]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Stable capability bit masks (J11).
|
|
2
|
+
|
|
3
|
+
Apple's audit_class file uses stable numeric bit masks. This module gives
|
|
4
|
+
Algo CLI the same ABI-style capability vocabulary for tools/kernels while
|
|
5
|
+
preserving human-readable tier names.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import IntEnum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Capability(IntEnum):
|
|
14
|
+
READ = 1 << 0
|
|
15
|
+
WRITE = 1 << 1
|
|
16
|
+
SHELL = 1 << 2
|
|
17
|
+
NETWORK = 1 << 3
|
|
18
|
+
MODEL = 1 << 4
|
|
19
|
+
CREDENTIAL = 1 << 5
|
|
20
|
+
MEMORY = 1 << 6
|
|
21
|
+
EXTERNAL_PUBLISH = 1 << 7
|
|
22
|
+
DESTRUCTIVE = 1 << 8
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
TIER_MASKS: dict[str, int] = {
|
|
26
|
+
"tier0": Capability.READ.value,
|
|
27
|
+
"tier1": Capability.READ.value | Capability.NETWORK.value | Capability.MODEL.value,
|
|
28
|
+
"tier2": Capability.READ.value | Capability.WRITE.value | Capability.SHELL.value | Capability.MODEL.value | Capability.MEMORY.value,
|
|
29
|
+
"tier3": sum(cap.value for cap in Capability),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class CapabilityMask:
|
|
35
|
+
value: int = 0
|
|
36
|
+
|
|
37
|
+
def has(self, capability: Capability) -> bool:
|
|
38
|
+
return bool(self.value & capability.value)
|
|
39
|
+
|
|
40
|
+
def add(self, capability: Capability) -> "CapabilityMask":
|
|
41
|
+
return CapabilityMask(self.value | capability.value)
|
|
42
|
+
|
|
43
|
+
def remove(self, capability: Capability) -> "CapabilityMask":
|
|
44
|
+
return CapabilityMask(self.value & ~capability.value)
|
|
45
|
+
|
|
46
|
+
def names(self) -> tuple[str, ...]:
|
|
47
|
+
return tuple(cap.name.lower() for cap in Capability if self.has(cap))
|
|
48
|
+
|
|
49
|
+
def to_dict(self) -> dict:
|
|
50
|
+
return {"value": self.value, "capabilities": list(self.names())}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def tier_mask(tier: str) -> int:
|
|
54
|
+
return TIER_MASKS.get((tier or "").strip().lower(), 0)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def mask_from_names(names: list[str] | tuple[str, ...]) -> CapabilityMask:
|
|
58
|
+
value = 0
|
|
59
|
+
for name in names or ():
|
|
60
|
+
key = str(name).strip().upper()
|
|
61
|
+
if key in Capability.__members__:
|
|
62
|
+
value |= Capability[key].value
|
|
63
|
+
return CapabilityMask(value)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
__all__ = ["Capability", "CapabilityMask", "TIER_MASKS", "mask_from_names", "tier_mask"]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Message and tool-call normalization for chat providers (Ollama / xAI shapes)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_attr(obj: Any, name: str, default: Any = None) -> Any:
|
|
10
|
+
if isinstance(obj, dict):
|
|
11
|
+
return obj.get(name, default)
|
|
12
|
+
return getattr(obj, name, default)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def normalize_tool_call(call: Any) -> tuple[str, dict[str, Any]]:
|
|
16
|
+
function = get_attr(call, "function", {})
|
|
17
|
+
name = get_attr(function, "name", "")
|
|
18
|
+
args = get_attr(function, "arguments", {}) or {}
|
|
19
|
+
if isinstance(args, str):
|
|
20
|
+
try:
|
|
21
|
+
args = json.loads(args)
|
|
22
|
+
except json.JSONDecodeError:
|
|
23
|
+
args = {"raw": args}
|
|
24
|
+
if not isinstance(args, dict):
|
|
25
|
+
args = {"raw": args}
|
|
26
|
+
return name, dict(args)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def collapse_tool_history_for_gemini(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
30
|
+
"""Rewrite assistant tool_calls + tool result messages into plain content turns.
|
|
31
|
+
|
|
32
|
+
Workaround for Ollama issue #14567 / PR #14676. Remove when the Ollama Python
|
|
33
|
+
SDK exposes thought_signature on tool calls.
|
|
34
|
+
"""
|
|
35
|
+
out: list[dict[str, Any]] = []
|
|
36
|
+
i = 0
|
|
37
|
+
while i < len(messages):
|
|
38
|
+
msg = messages[i]
|
|
39
|
+
if msg.get("role") == "assistant" and msg.get("tool_calls"):
|
|
40
|
+
summary_lines: list[str] = []
|
|
41
|
+
if msg.get("content"):
|
|
42
|
+
summary_lines.append(str(msg["content"]))
|
|
43
|
+
for tc in msg.get("tool_calls", []):
|
|
44
|
+
fn = tc.get("function", {}) if isinstance(tc, dict) else {}
|
|
45
|
+
fn_name = fn.get("name", "?")
|
|
46
|
+
fn_args = fn.get("arguments", {})
|
|
47
|
+
try:
|
|
48
|
+
args_preview = json.dumps(fn_args, ensure_ascii=True, default=str)[:120]
|
|
49
|
+
except Exception:
|
|
50
|
+
args_preview = str(fn_args)[:120]
|
|
51
|
+
summary_lines.append(f"[Previously called {fn_name}({args_preview})]")
|
|
52
|
+
j = i + 1
|
|
53
|
+
while j < len(messages) and messages[j].get("role") == "tool":
|
|
54
|
+
tr = messages[j]
|
|
55
|
+
tool_name = tr.get("name", "?")
|
|
56
|
+
tool_content = str(tr.get("content", "")).strip()
|
|
57
|
+
preview = tool_content[:600] + ("…" if len(tool_content) > 600 else "")
|
|
58
|
+
summary_lines.append(f"[{tool_name} result] {preview}")
|
|
59
|
+
j += 1
|
|
60
|
+
out.append({"role": "assistant", "content": "\n".join(summary_lines)})
|
|
61
|
+
i = j
|
|
62
|
+
else:
|
|
63
|
+
out.append(msg)
|
|
64
|
+
i += 1
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _coerce_arguments_dict(args: Any) -> dict[str, Any]:
|
|
69
|
+
"""Tool-call arguments must serialize as a dict: the Ollama SDK's Message
|
|
70
|
+
model rejects string arguments, but OpenAI-style streams (xAI) accumulate
|
|
71
|
+
arguments as a JSON string."""
|
|
72
|
+
if isinstance(args, dict):
|
|
73
|
+
return args
|
|
74
|
+
if isinstance(args, str):
|
|
75
|
+
try:
|
|
76
|
+
parsed = json.loads(args)
|
|
77
|
+
except json.JSONDecodeError:
|
|
78
|
+
return {"raw": args}
|
|
79
|
+
return parsed if isinstance(parsed, dict) else {"raw": parsed}
|
|
80
|
+
return {}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def serialize_tool_call(call: Any) -> dict[str, Any]:
|
|
84
|
+
if isinstance(call, dict):
|
|
85
|
+
out_dict: dict[str, Any] = json.loads(json.dumps(call, ensure_ascii=False, default=str))
|
|
86
|
+
fn = out_dict.get("function")
|
|
87
|
+
if isinstance(fn, dict):
|
|
88
|
+
fn["arguments"] = _coerce_arguments_dict(fn.get("arguments"))
|
|
89
|
+
return out_dict
|
|
90
|
+
function = get_attr(call, "function", {})
|
|
91
|
+
name = get_attr(function, "name", "")
|
|
92
|
+
args_payload = _coerce_arguments_dict(get_attr(function, "arguments", {}) or {})
|
|
93
|
+
out: dict[str, Any] = {"function": {"name": name, "arguments": args_payload}}
|
|
94
|
+
call_id = get_attr(call, "id", None)
|
|
95
|
+
if call_id:
|
|
96
|
+
out["id"] = call_id
|
|
97
|
+
call_type = get_attr(call, "type", None)
|
|
98
|
+
if call_type:
|
|
99
|
+
out["type"] = call_type
|
|
100
|
+
for sig_key in ("thought_signature", "thoughtSignature"):
|
|
101
|
+
sig = get_attr(call, sig_key, None) or get_attr(function, sig_key, None)
|
|
102
|
+
if sig:
|
|
103
|
+
out[sig_key] = sig
|
|
104
|
+
break
|
|
105
|
+
return out
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def normalize_message(message: Any) -> dict[str, Any]:
|
|
109
|
+
if isinstance(message, dict):
|
|
110
|
+
return message
|
|
111
|
+
out: dict[str, Any] = {}
|
|
112
|
+
for key in ("role", "content", "thinking", "tool_calls"):
|
|
113
|
+
value = getattr(message, key, None)
|
|
114
|
+
if value:
|
|
115
|
+
out[key] = value
|
|
116
|
+
return out
|