cluxion-agentplugin-preprocessing 0.2.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.
- cluxion_agentplugin_adapters/claude/.claude-plugin/plugin.json +8 -0
- cluxion_agentplugin_adapters/claude/skills/preprocess/SKILL.md +33 -0
- cluxion_agentplugin_adapters/codex/config-snippet.toml +5 -0
- cluxion_agentplugin_docs/cluxion-Docs/README.md +22 -0
- cluxion_agentplugin_docs/cluxion-Docs/architecture.md +36 -0
- cluxion_agentplugin_docs/cluxion-Docs/harness-logic.md +51 -0
- cluxion_agentplugin_docs/cluxion-Docs/honesty-preprocessing.md +40 -0
- cluxion_agentplugin_docs/cluxion-Docs/install-and-operations.md +36 -0
- cluxion_agentplugin_docs/cluxion-Docs/security.md +27 -0
- cluxion_agentplugin_docs/github-profile/README.md +67 -0
- cluxion_agentplugin_preprocessing/__init__.py +7 -0
- cluxion_agentplugin_preprocessing/cli.py +124 -0
- cluxion_agentplugin_preprocessing/hermes_config.py +163 -0
- cluxion_agentplugin_preprocessing/plugin.py +135 -0
- cluxion_agentplugin_preprocessing/plugin.yaml +13 -0
- cluxion_agentplugin_preprocessing/runner.py +241 -0
- cluxion_agentplugin_preprocessing/schemas.py +148 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/METADATA +115 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/RECORD +48 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/WHEEL +4 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/entry_points.txt +8 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/licenses/LICENSE +197 -0
- cluxion_runtime/__init__.py +16 -0
- cluxion_runtime/__main__.py +5 -0
- cluxion_runtime/adapters/__init__.py +25 -0
- cluxion_runtime/adapters/contract.py +82 -0
- cluxion_runtime/adapters/grok_build.py +35 -0
- cluxion_runtime/adapters/hermes.py +161 -0
- cluxion_runtime/adapters/spec.py +35 -0
- cluxion_runtime/bootstrap.py +270 -0
- cluxion_runtime/cli.py +282 -0
- cluxion_runtime/core/__init__.py +36 -0
- cluxion_runtime/core/clarification.py +192 -0
- cluxion_runtime/core/dispatch_store.py +270 -0
- cluxion_runtime/core/harness.py +320 -0
- cluxion_runtime/core/intent.py +55 -0
- cluxion_runtime/core/ledger.py +189 -0
- cluxion_runtime/core/ledger_codec.py +38 -0
- cluxion_runtime/core/plan_codec.py +121 -0
- cluxion_runtime/core/preprocess.py +497 -0
- cluxion_runtime/core/types.py +220 -0
- cluxion_runtime/core/work_queue.py +73 -0
- cluxion_runtime/models/__init__.py +15 -0
- cluxion_runtime/models/supervisor.py +156 -0
- cluxion_runtime/models/vllm_mlx.py +87 -0
- cluxion_runtime/resources/__init__.py +7 -0
- cluxion_runtime/resources/queue_bridge.py +128 -0
- cluxion_runtime/resources/rust_bridge.py +82 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""HarnessPlan을 외부 adapter용 JSON 객체로 변환한다."""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from cluxion_runtime.core.ledger_codec import item_to_dict
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from cluxion_runtime.core.types import HarnessPlan
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def plan_to_dict(plan: HarnessPlan) -> dict[str, object]:
|
|
14
|
+
"""HarnessPlan의 공개 필드만 JSON 안전 객체로 변환한다."""
|
|
15
|
+
return {
|
|
16
|
+
"item": _public_item(plan),
|
|
17
|
+
"queue_position": plan.queue_position,
|
|
18
|
+
"queue_backend": plan.queue_backend,
|
|
19
|
+
"clarification": {
|
|
20
|
+
"required": plan.clarification_required,
|
|
21
|
+
"questions": list(plan.clarification_questions),
|
|
22
|
+
},
|
|
23
|
+
"intent": {
|
|
24
|
+
"category": plan.intent.category,
|
|
25
|
+
"operation": plan.intent.operation,
|
|
26
|
+
"local_model_requested": plan.intent.local_model_requested,
|
|
27
|
+
"direction": plan.intent.direction,
|
|
28
|
+
"confidence": plan.intent.confidence,
|
|
29
|
+
"signals": list(plan.intent.signals),
|
|
30
|
+
},
|
|
31
|
+
"preprocessing": {
|
|
32
|
+
"normalized_prompt": plan.preprocessing.normalized_prompt,
|
|
33
|
+
"token_estimate": plan.preprocessing.token_estimate,
|
|
34
|
+
"split_required": plan.preprocessing.split_required,
|
|
35
|
+
"effort": plan.preprocessing.effort,
|
|
36
|
+
"mode": plan.preprocessing.mode,
|
|
37
|
+
"preprocess_required": plan.preprocessing.preprocess_required,
|
|
38
|
+
"reason_codes": list(plan.preprocessing.reason_codes),
|
|
39
|
+
"answer_policy": {
|
|
40
|
+
"unknown_behavior": plan.preprocessing.answer_policy.unknown_behavior,
|
|
41
|
+
"source_policy": plan.preprocessing.answer_policy.source_policy,
|
|
42
|
+
"scope": plan.preprocessing.answer_policy.scope,
|
|
43
|
+
"response_contract": plan.preprocessing.answer_policy.response_contract,
|
|
44
|
+
"verification_required": plan.preprocessing.answer_policy.verification_required,
|
|
45
|
+
"citation_required": plan.preprocessing.answer_policy.citation_required,
|
|
46
|
+
"uncertainty_level": plan.preprocessing.answer_policy.uncertainty_level,
|
|
47
|
+
"required_checks": list(plan.preprocessing.answer_policy.required_checks),
|
|
48
|
+
"grounding": list(plan.preprocessing.answer_policy.grounding),
|
|
49
|
+
"rules": list(plan.preprocessing.answer_policy.rules),
|
|
50
|
+
},
|
|
51
|
+
"segments": [
|
|
52
|
+
{
|
|
53
|
+
"segment_id": segment.segment_id,
|
|
54
|
+
"char_start": segment.char_start,
|
|
55
|
+
"char_end": segment.char_end,
|
|
56
|
+
"token_estimate": segment.token_estimate,
|
|
57
|
+
"checksum": segment.checksum,
|
|
58
|
+
"preview": segment.preview,
|
|
59
|
+
}
|
|
60
|
+
for segment in plan.preprocessing.segments
|
|
61
|
+
],
|
|
62
|
+
"evidence": list(plan.preprocessing.evidence),
|
|
63
|
+
},
|
|
64
|
+
"resource": {
|
|
65
|
+
"allowed": plan.resource.allowed,
|
|
66
|
+
"mode": plan.resource.mode,
|
|
67
|
+
"reason": plan.resource.reason,
|
|
68
|
+
"recommended_parallel": plan.resource.recommended_parallel,
|
|
69
|
+
"work_kind": plan.resource.work_kind,
|
|
70
|
+
"dispatch_memory_budget_mb": plan.resource.dispatch_memory_budget_mb,
|
|
71
|
+
"reason_codes": list(plan.resource.reason_codes),
|
|
72
|
+
},
|
|
73
|
+
"runtime": {
|
|
74
|
+
"kind": plan.runtime.kind.value,
|
|
75
|
+
"model": plan.runtime.model,
|
|
76
|
+
"base_url": plan.runtime.base_url,
|
|
77
|
+
"command": list(plan.runtime.command),
|
|
78
|
+
"health_path": plan.runtime.health_path,
|
|
79
|
+
},
|
|
80
|
+
"host_execution": {
|
|
81
|
+
"model_owner": plan.execution.model_owner,
|
|
82
|
+
"provider_policy": plan.execution.provider_policy,
|
|
83
|
+
"strategy": plan.execution.strategy,
|
|
84
|
+
"queue_required": plan.execution.queue_required,
|
|
85
|
+
"synthesis_required": plan.execution.synthesis_required,
|
|
86
|
+
"preflight_required": plan.execution.preflight_required,
|
|
87
|
+
"max_extra_model_calls": plan.execution.max_extra_model_calls,
|
|
88
|
+
"next_tool": plan.execution.next_tool,
|
|
89
|
+
"record_tool": plan.execution.record_tool,
|
|
90
|
+
"brief_tool": plan.execution.brief_tool,
|
|
91
|
+
"performance_notes": list(plan.execution.performance_notes),
|
|
92
|
+
"steps": [
|
|
93
|
+
{
|
|
94
|
+
"step_id": step.step_id,
|
|
95
|
+
"kind": step.kind,
|
|
96
|
+
"prompt": step.prompt,
|
|
97
|
+
"segment_id": step.segment_id,
|
|
98
|
+
"checksum": step.checksum,
|
|
99
|
+
"token_estimate": step.token_estimate,
|
|
100
|
+
"depends_on": list(step.depends_on),
|
|
101
|
+
"required_checks": list(step.required_checks),
|
|
102
|
+
}
|
|
103
|
+
for step in plan.execution.steps
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _public_item(plan: HarnessPlan) -> dict[str, object]:
|
|
110
|
+
item = item_to_dict(plan.item)
|
|
111
|
+
if not plan.execution.queue_required:
|
|
112
|
+
item["prompt_redacted"] = False
|
|
113
|
+
return item
|
|
114
|
+
item["prompt"] = plan.preprocessing.normalized_prompt
|
|
115
|
+
item["prompt_redacted"] = True
|
|
116
|
+
item["original_prompt_stored"] = False
|
|
117
|
+
item["original_prompt_out_of_band_required"] = True
|
|
118
|
+
return item
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
__all__ = ["plan_to_dict"]
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""AI 작업 전처리와 장문 작업큐 세그먼트 생성."""
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
|
|
7
|
+
from cluxion_runtime.core.types import AnswerPolicy, PreprocessResult, QueueSegment, WorkItem, WorkPriority
|
|
8
|
+
|
|
9
|
+
_SIMPLE_PROMPT_CHAR_LIMIT = 800
|
|
10
|
+
_SIMPLE_PROMPT_TOKEN_LIMIT = 160
|
|
11
|
+
_SIMPLE_CONTEXT_TOKEN_LIMIT = 512
|
|
12
|
+
_VERIFICATION_PROMPT_CHAR_LIMIT = 1_200
|
|
13
|
+
_VERIFICATION_PROMPT_TOKEN_LIMIT = 240
|
|
14
|
+
_SIGNAL_SCAN_CHAR_LIMIT = 4_096
|
|
15
|
+
_PREPROCESS_INTENT_CATEGORIES = {"engineering", "security", "documentation", "local_model"}
|
|
16
|
+
_PREPROCESS_KEYWORDS = (
|
|
17
|
+
"audit",
|
|
18
|
+
"benchmark",
|
|
19
|
+
"build",
|
|
20
|
+
"code",
|
|
21
|
+
"debug",
|
|
22
|
+
"deploy",
|
|
23
|
+
"fix",
|
|
24
|
+
"implement",
|
|
25
|
+
"install",
|
|
26
|
+
"patch",
|
|
27
|
+
"pytest",
|
|
28
|
+
"refactor",
|
|
29
|
+
"security",
|
|
30
|
+
"test",
|
|
31
|
+
"vllm",
|
|
32
|
+
"vllm-mlx",
|
|
33
|
+
"구현",
|
|
34
|
+
"긴",
|
|
35
|
+
"디버그",
|
|
36
|
+
"리팩터",
|
|
37
|
+
"문서",
|
|
38
|
+
"배포",
|
|
39
|
+
"보안",
|
|
40
|
+
"설치",
|
|
41
|
+
"수정",
|
|
42
|
+
"업그레이드",
|
|
43
|
+
"전처리",
|
|
44
|
+
"점검",
|
|
45
|
+
"테스트",
|
|
46
|
+
"패치",
|
|
47
|
+
)
|
|
48
|
+
_CURRENT_FACT_KEYWORDS = (
|
|
49
|
+
"current",
|
|
50
|
+
"latest",
|
|
51
|
+
"recent",
|
|
52
|
+
"release",
|
|
53
|
+
"today",
|
|
54
|
+
"version",
|
|
55
|
+
"최신",
|
|
56
|
+
"최근",
|
|
57
|
+
"오늘",
|
|
58
|
+
"현재",
|
|
59
|
+
"릴리즈",
|
|
60
|
+
"버전",
|
|
61
|
+
)
|
|
62
|
+
_EXTERNAL_SOURCE_KEYWORDS = (
|
|
63
|
+
"docs",
|
|
64
|
+
"github",
|
|
65
|
+
"news",
|
|
66
|
+
"paper",
|
|
67
|
+
"pypi",
|
|
68
|
+
"reference",
|
|
69
|
+
"source",
|
|
70
|
+
"url",
|
|
71
|
+
"논문",
|
|
72
|
+
"뉴스",
|
|
73
|
+
"문서",
|
|
74
|
+
"소스",
|
|
75
|
+
"출처",
|
|
76
|
+
"파이피",
|
|
77
|
+
"깃허브",
|
|
78
|
+
"링크",
|
|
79
|
+
)
|
|
80
|
+
_RUNTIME_STATE_KEYWORDS = (
|
|
81
|
+
"ci",
|
|
82
|
+
"command",
|
|
83
|
+
"env",
|
|
84
|
+
"file",
|
|
85
|
+
"installed",
|
|
86
|
+
"log",
|
|
87
|
+
"output",
|
|
88
|
+
"path",
|
|
89
|
+
"port",
|
|
90
|
+
"process",
|
|
91
|
+
"terminal",
|
|
92
|
+
"venv",
|
|
93
|
+
"경로",
|
|
94
|
+
"로그",
|
|
95
|
+
"명령",
|
|
96
|
+
"설치",
|
|
97
|
+
"실행",
|
|
98
|
+
"출력",
|
|
99
|
+
"파일",
|
|
100
|
+
"포트",
|
|
101
|
+
"프로세스",
|
|
102
|
+
"환경",
|
|
103
|
+
)
|
|
104
|
+
_CHECK_REQUEST_KEYWORDS = (
|
|
105
|
+
"check",
|
|
106
|
+
"confirm",
|
|
107
|
+
"inspect",
|
|
108
|
+
"look up",
|
|
109
|
+
"search",
|
|
110
|
+
"verify",
|
|
111
|
+
"검사",
|
|
112
|
+
"검색",
|
|
113
|
+
"보기",
|
|
114
|
+
"점검",
|
|
115
|
+
"확인",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def preprocess_work(
|
|
120
|
+
item: WorkItem,
|
|
121
|
+
*,
|
|
122
|
+
max_segment_chars: int = 72_000,
|
|
123
|
+
max_segment_tokens: int = 32_000,
|
|
124
|
+
intent_category: str | None = None,
|
|
125
|
+
local_model_requested: bool = False,
|
|
126
|
+
force_mode: str | None = None,
|
|
127
|
+
) -> PreprocessResult:
|
|
128
|
+
"""작업을 모델 친화적인 지시와 세그먼트 증거로 정규화한다."""
|
|
129
|
+
token_estimate = estimate_tokens(item.prompt)
|
|
130
|
+
split_required = len(item.prompt) > max_segment_chars or token_estimate > max_segment_tokens
|
|
131
|
+
signal_text = _bounded_signal_text(item.prompt)
|
|
132
|
+
category = intent_category or _lightweight_intent_category(signal_text)
|
|
133
|
+
verification_signals = _verification_signals(signal_text)
|
|
134
|
+
if force_mode == "needs_clarification":
|
|
135
|
+
mode, preprocess_required, reason_codes = "needs_clarification", False, ("clarification_required",)
|
|
136
|
+
else:
|
|
137
|
+
mode, preprocess_required, reason_codes = _preprocess_policy(
|
|
138
|
+
item,
|
|
139
|
+
token_estimate=token_estimate,
|
|
140
|
+
split_required=split_required,
|
|
141
|
+
intent_category=category,
|
|
142
|
+
local_model_requested=local_model_requested,
|
|
143
|
+
verification_signals=verification_signals,
|
|
144
|
+
)
|
|
145
|
+
segments = _segments_for(item.prompt, max_segment_chars, max_segment_tokens, split_required, preprocess_required)
|
|
146
|
+
normalized = _normalized_prompt(item, segments, token_estimate, split_required)
|
|
147
|
+
evidence = (
|
|
148
|
+
tuple(f"{segment.segment_id}:{segment.checksum}:{segment.token_estimate}" for segment in segments)
|
|
149
|
+
if preprocess_required
|
|
150
|
+
else ()
|
|
151
|
+
)
|
|
152
|
+
return PreprocessResult(
|
|
153
|
+
normalized_prompt=normalized,
|
|
154
|
+
segments=segments,
|
|
155
|
+
token_estimate=token_estimate,
|
|
156
|
+
split_required=split_required,
|
|
157
|
+
effort=_effort_for(item, token_estimate, split_required, mode=mode, preprocess_required=preprocess_required),
|
|
158
|
+
evidence=evidence,
|
|
159
|
+
mode=mode,
|
|
160
|
+
preprocess_required=preprocess_required,
|
|
161
|
+
reason_codes=reason_codes,
|
|
162
|
+
answer_policy=_answer_policy_for(
|
|
163
|
+
item,
|
|
164
|
+
intent_category=category,
|
|
165
|
+
mode=mode,
|
|
166
|
+
split_required=split_required,
|
|
167
|
+
reason_codes=reason_codes,
|
|
168
|
+
verification_signals=verification_signals,
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def estimate_tokens(text: str) -> int:
|
|
174
|
+
"""Estimate tokens conservatively without depending on the old Cluxion OS package."""
|
|
175
|
+
cjk = sum(1 for ch in text if ord(ch) > 127)
|
|
176
|
+
ascii_chars = max(0, len(text) - cjk)
|
|
177
|
+
return max(1, cjk + ascii_chars // 4)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _preprocess_policy(
|
|
181
|
+
item: WorkItem,
|
|
182
|
+
*,
|
|
183
|
+
token_estimate: int,
|
|
184
|
+
split_required: bool,
|
|
185
|
+
intent_category: str | None,
|
|
186
|
+
local_model_requested: bool,
|
|
187
|
+
verification_signals: tuple[str, ...],
|
|
188
|
+
) -> tuple[str, bool, tuple[str, ...]]:
|
|
189
|
+
category = intent_category or "general"
|
|
190
|
+
route = item.model_route.lower()
|
|
191
|
+
if split_required:
|
|
192
|
+
return "queued", True, ("split_required",)
|
|
193
|
+
if local_model_requested or _explicit_local_route(route):
|
|
194
|
+
return "standard", True, ("local_model_route",)
|
|
195
|
+
if item.priority <= WorkPriority.HIGH:
|
|
196
|
+
return "standard", True, ("priority_requires_harness",)
|
|
197
|
+
if item.expected_ram_mb > 0:
|
|
198
|
+
return "standard", True, ("explicit_memory_budget",)
|
|
199
|
+
if item.context_tokens > _SIMPLE_CONTEXT_TOKEN_LIMIT:
|
|
200
|
+
return "standard", True, ("large_context",)
|
|
201
|
+
if _can_use_verification_fast_path(item, token_estimate, category, verification_signals):
|
|
202
|
+
return "verification_answer", False, ("verification_required", *verification_signals)
|
|
203
|
+
if category in _PREPROCESS_INTENT_CATEGORIES:
|
|
204
|
+
return "standard", True, (f"intent_{category}",)
|
|
205
|
+
if token_estimate > _SIMPLE_PROMPT_TOKEN_LIMIT:
|
|
206
|
+
return "standard", True, ("prompt_token_threshold",)
|
|
207
|
+
if len(item.prompt) > _SIMPLE_PROMPT_CHAR_LIMIT:
|
|
208
|
+
return "standard", True, ("prompt_char_threshold",)
|
|
209
|
+
if _has_any(item.prompt.lower(), _PREPROCESS_KEYWORDS):
|
|
210
|
+
return "standard", True, ("preprocess_keyword",)
|
|
211
|
+
return "simple_answer", False, ("short_prompt", "no_tool_or_local_signals")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _can_use_verification_fast_path(
|
|
215
|
+
item: WorkItem,
|
|
216
|
+
token_estimate: int,
|
|
217
|
+
category: str,
|
|
218
|
+
verification_signals: tuple[str, ...],
|
|
219
|
+
) -> bool:
|
|
220
|
+
if not verification_signals:
|
|
221
|
+
return False
|
|
222
|
+
if category in {"engineering", "security", "local_model"}:
|
|
223
|
+
return False
|
|
224
|
+
if token_estimate > _VERIFICATION_PROMPT_TOKEN_LIMIT:
|
|
225
|
+
return False
|
|
226
|
+
return len(item.prompt) <= _VERIFICATION_PROMPT_CHAR_LIMIT
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _answer_policy_for(
|
|
230
|
+
item: WorkItem,
|
|
231
|
+
*,
|
|
232
|
+
intent_category: str,
|
|
233
|
+
mode: str,
|
|
234
|
+
split_required: bool,
|
|
235
|
+
reason_codes: tuple[str, ...],
|
|
236
|
+
verification_signals: tuple[str, ...],
|
|
237
|
+
) -> AnswerPolicy:
|
|
238
|
+
required_checks = _required_checks_for(
|
|
239
|
+
item,
|
|
240
|
+
intent_category=intent_category,
|
|
241
|
+
mode=mode,
|
|
242
|
+
split_required=split_required,
|
|
243
|
+
verification_signals=verification_signals,
|
|
244
|
+
)
|
|
245
|
+
verification_required = bool(required_checks)
|
|
246
|
+
citation_required = "external_source" in verification_signals or "current_fact" in verification_signals
|
|
247
|
+
uncertainty_level = _uncertainty_level_for(
|
|
248
|
+
mode=mode,
|
|
249
|
+
verification_required=verification_required,
|
|
250
|
+
citation_required=citation_required,
|
|
251
|
+
split_required=split_required,
|
|
252
|
+
)
|
|
253
|
+
return AnswerPolicy(
|
|
254
|
+
response_contract=_response_contract_for(
|
|
255
|
+
mode=mode,
|
|
256
|
+
verification_required=verification_required,
|
|
257
|
+
citation_required=citation_required,
|
|
258
|
+
),
|
|
259
|
+
verification_required=verification_required,
|
|
260
|
+
citation_required=citation_required,
|
|
261
|
+
uncertainty_level=uncertainty_level,
|
|
262
|
+
required_checks=required_checks,
|
|
263
|
+
rules=_rules_for(
|
|
264
|
+
mode=mode,
|
|
265
|
+
verification_required=verification_required,
|
|
266
|
+
citation_required=citation_required,
|
|
267
|
+
reason_codes=reason_codes,
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _required_checks_for(
|
|
273
|
+
item: WorkItem,
|
|
274
|
+
*,
|
|
275
|
+
intent_category: str,
|
|
276
|
+
mode: str,
|
|
277
|
+
split_required: bool,
|
|
278
|
+
verification_signals: tuple[str, ...],
|
|
279
|
+
) -> tuple[str, ...]:
|
|
280
|
+
checks: list[str] = []
|
|
281
|
+
if "current_fact" in verification_signals:
|
|
282
|
+
checks.append("verify_current_or_recent_fact")
|
|
283
|
+
if "external_source" in verification_signals:
|
|
284
|
+
checks.append("cite_external_source_or_document")
|
|
285
|
+
if "runtime_state" in verification_signals:
|
|
286
|
+
checks.append("inspect_runtime_state_before_claiming")
|
|
287
|
+
if "explicit_check_request" in verification_signals:
|
|
288
|
+
checks.append("run_requested_check_or_state_not_run")
|
|
289
|
+
if intent_category == "engineering":
|
|
290
|
+
checks.append("tie_claims_to_file_diff_or_command_output")
|
|
291
|
+
if intent_category == "security":
|
|
292
|
+
checks.append("tie_security_claims_to_evidence")
|
|
293
|
+
if mode == "queued" or split_required:
|
|
294
|
+
checks.append("preserve_segment_checksums_in_synthesis")
|
|
295
|
+
if _explicit_local_route(item.model_route.lower()):
|
|
296
|
+
checks.append("verify_local_model_endpoint_before_claiming_ready")
|
|
297
|
+
return tuple(dict.fromkeys(checks))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _response_contract_for(*, mode: str, verification_required: bool, citation_required: bool) -> str:
|
|
301
|
+
if mode == "queued":
|
|
302
|
+
return "synthesize_from_segment_evidence"
|
|
303
|
+
if verification_required and citation_required:
|
|
304
|
+
return "verify_and_cite_before_answer"
|
|
305
|
+
if verification_required:
|
|
306
|
+
return "verify_before_answer"
|
|
307
|
+
if mode == "verification_answer":
|
|
308
|
+
return "verify_before_answer"
|
|
309
|
+
return "direct_answer_with_uncertainty_boundary"
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _uncertainty_level_for(
|
|
313
|
+
*,
|
|
314
|
+
mode: str,
|
|
315
|
+
verification_required: bool,
|
|
316
|
+
citation_required: bool,
|
|
317
|
+
split_required: bool,
|
|
318
|
+
) -> str:
|
|
319
|
+
if split_required or mode == "queued":
|
|
320
|
+
return "high"
|
|
321
|
+
if verification_required or citation_required:
|
|
322
|
+
return "medium"
|
|
323
|
+
return "low"
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _rules_for(
|
|
327
|
+
*,
|
|
328
|
+
mode: str,
|
|
329
|
+
verification_required: bool,
|
|
330
|
+
citation_required: bool,
|
|
331
|
+
reason_codes: tuple[str, ...],
|
|
332
|
+
) -> tuple[str, ...]:
|
|
333
|
+
rules = [
|
|
334
|
+
"If the available context is insufficient, say that clearly before proceeding.",
|
|
335
|
+
"Do not fabricate file state, external facts, tool results, or model availability.",
|
|
336
|
+
"For current or environment-specific claims, verify through tools before presenting them as facts.",
|
|
337
|
+
"Separate verified facts from inferences and unknowns when accuracy matters.",
|
|
338
|
+
"If a check was not run, say it was not run; do not imply that it passed.",
|
|
339
|
+
]
|
|
340
|
+
if verification_required:
|
|
341
|
+
rules.append("Run the required checks before making the requested factual claim.")
|
|
342
|
+
if citation_required:
|
|
343
|
+
rules.append("Attach source references for external or time-sensitive claims when the host surface supports it.")
|
|
344
|
+
if mode == "verification_answer":
|
|
345
|
+
rules.append("Keep the answer short after verification; do not expand into unrelated planning.")
|
|
346
|
+
if "verification_required" in reason_codes:
|
|
347
|
+
rules.append("Treat short verification prompts as fact-finding, not as ordinary simple chat.")
|
|
348
|
+
return tuple(dict.fromkeys(rules))
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _segments_for(
|
|
352
|
+
prompt: str,
|
|
353
|
+
max_segment_chars: int,
|
|
354
|
+
max_segment_tokens: int,
|
|
355
|
+
split_required: bool,
|
|
356
|
+
preprocess_required: bool,
|
|
357
|
+
) -> tuple[QueueSegment, ...]:
|
|
358
|
+
if not preprocess_required:
|
|
359
|
+
return ()
|
|
360
|
+
if split_required:
|
|
361
|
+
return tuple(_split_segments(prompt, max_segment_chars, max_segment_tokens))
|
|
362
|
+
return (_segment("seg_000", prompt, 0, len(prompt)),)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _split_segments(text: str, max_chars: int, max_tokens: int) -> list[QueueSegment]:
|
|
366
|
+
segments: list[QueueSegment] = []
|
|
367
|
+
start = 0
|
|
368
|
+
while start < len(text):
|
|
369
|
+
end = min(len(text), start + max_chars)
|
|
370
|
+
boundary = _find_boundary(text, start, end)
|
|
371
|
+
chunk = text[start:boundary].strip()
|
|
372
|
+
if chunk:
|
|
373
|
+
segment = _segment(f"seg_{len(segments):03d}", chunk, start, boundary)
|
|
374
|
+
if segment.token_estimate > max_tokens and max_chars > 1_024:
|
|
375
|
+
segments.extend(_split_segments(chunk, max(1_024, max_chars // 2), max_tokens))
|
|
376
|
+
else:
|
|
377
|
+
segments.append(segment)
|
|
378
|
+
start = max(boundary, start + 1)
|
|
379
|
+
return segments
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _find_boundary(text: str, start: int, end: int) -> int:
|
|
383
|
+
if end >= len(text):
|
|
384
|
+
return len(text)
|
|
385
|
+
window = text[start:end]
|
|
386
|
+
for marker in ("\n\n", "\n", ". ", "? ", "! ", "。"):
|
|
387
|
+
index = window.rfind(marker)
|
|
388
|
+
if index > max(200, len(window) // 2):
|
|
389
|
+
return start + index + len(marker)
|
|
390
|
+
return end
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _segment(segment_id: str, content: str, start: int, end: int) -> QueueSegment:
|
|
394
|
+
checksum = hashlib.sha256(content.encode("utf-8")).hexdigest()[:16]
|
|
395
|
+
return QueueSegment(
|
|
396
|
+
segment_id=segment_id,
|
|
397
|
+
char_start=start,
|
|
398
|
+
char_end=end,
|
|
399
|
+
token_estimate=estimate_tokens(content),
|
|
400
|
+
checksum=checksum,
|
|
401
|
+
preview=_summarize_edges(content, 480),
|
|
402
|
+
content=content,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _normalized_prompt(
|
|
407
|
+
item: WorkItem,
|
|
408
|
+
segments: tuple[QueueSegment, ...],
|
|
409
|
+
token_estimate: int,
|
|
410
|
+
split_required: bool,
|
|
411
|
+
) -> str:
|
|
412
|
+
if not split_required:
|
|
413
|
+
return item.prompt
|
|
414
|
+
lines = [
|
|
415
|
+
"[cluxion_queue_required]",
|
|
416
|
+
f"work_id={item.work_id}",
|
|
417
|
+
f"surface={item.surface}",
|
|
418
|
+
f"priority={item.priority.name.lower()}",
|
|
419
|
+
f"estimated_tokens={token_estimate}",
|
|
420
|
+
f"segment_count={len(segments)}",
|
|
421
|
+
"Process segments in order, preserve evidence, then synthesize.",
|
|
422
|
+
"[segment_index]",
|
|
423
|
+
]
|
|
424
|
+
lines.extend(f"{segment.segment_id} checksum={segment.checksum} preview={segment.preview}" for segment in segments)
|
|
425
|
+
return "\n".join(lines)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _effort_for(
|
|
429
|
+
item: WorkItem,
|
|
430
|
+
token_estimate: int,
|
|
431
|
+
split_required: bool,
|
|
432
|
+
*,
|
|
433
|
+
mode: str,
|
|
434
|
+
preprocess_required: bool,
|
|
435
|
+
) -> str:
|
|
436
|
+
if not preprocess_required or mode == "simple_answer":
|
|
437
|
+
return "simple"
|
|
438
|
+
if item.priority <= WorkPriority.HIGH or split_required or token_estimate > 24_000:
|
|
439
|
+
return "high"
|
|
440
|
+
if token_estimate > 6_000:
|
|
441
|
+
return "medium"
|
|
442
|
+
return "low"
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _summarize_edges(text: str, max_chars: int) -> str:
|
|
446
|
+
clean = " ".join(text.split())
|
|
447
|
+
if len(clean) <= max_chars:
|
|
448
|
+
return clean
|
|
449
|
+
edge = max(120, max_chars // 2)
|
|
450
|
+
return f"{clean[:edge]} ... {clean[-edge:]}"
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _lightweight_intent_category(prompt: str) -> str:
|
|
454
|
+
text = prompt
|
|
455
|
+
if _has_any(text, ("vllm-mlx", "mlx", "local model", "로컬모델", "로컬 모델", "serve-local")):
|
|
456
|
+
return "local_model"
|
|
457
|
+
if _has_any(text, ("security", "audit", "vulnerability", "보안", "취약점", "시크릿")):
|
|
458
|
+
return "security"
|
|
459
|
+
if _has_any(text, ("test", "pytest", "unit", "테스트", "검증")):
|
|
460
|
+
return "engineering"
|
|
461
|
+
if _has_any(text, ("code", "implement", "fix", "refactor", "패치", "수정", "구현", "리팩터")):
|
|
462
|
+
return "engineering"
|
|
463
|
+
if _has_any(text, ("docs", "readme", "문서", "가이드")):
|
|
464
|
+
return "documentation"
|
|
465
|
+
return "general"
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _verification_signals(prompt: str) -> tuple[str, ...]:
|
|
469
|
+
text = prompt
|
|
470
|
+
signals: list[str] = []
|
|
471
|
+
if _has_any(text, _CURRENT_FACT_KEYWORDS):
|
|
472
|
+
signals.append("current_fact")
|
|
473
|
+
if _has_any(text, _EXTERNAL_SOURCE_KEYWORDS):
|
|
474
|
+
signals.append("external_source")
|
|
475
|
+
if _has_any(text, _RUNTIME_STATE_KEYWORDS):
|
|
476
|
+
signals.append("runtime_state")
|
|
477
|
+
if _has_any(text, _CHECK_REQUEST_KEYWORDS):
|
|
478
|
+
signals.append("explicit_check_request")
|
|
479
|
+
return tuple(dict.fromkeys(signals))
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _bounded_signal_text(prompt: str) -> str:
|
|
483
|
+
if len(prompt) <= _SIGNAL_SCAN_CHAR_LIMIT:
|
|
484
|
+
return prompt.lower()
|
|
485
|
+
edge = max(1, _SIGNAL_SCAN_CHAR_LIMIT // 2)
|
|
486
|
+
return f"{prompt[:edge]}\n{prompt[-edge:]}".lower()
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _explicit_local_route(route: str) -> bool:
|
|
490
|
+
return route.startswith(("local/", "mlx/", "vllm-mlx/", "vllm_mlx/")) and route != "local/default"
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _has_any(text: str, needles: tuple[str, ...]) -> bool:
|
|
494
|
+
return any(needle in text for needle in needles)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
__all__ = ["estimate_tokens", "preprocess_work"]
|