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.
Files changed (48) hide show
  1. cluxion_agentplugin_adapters/claude/.claude-plugin/plugin.json +8 -0
  2. cluxion_agentplugin_adapters/claude/skills/preprocess/SKILL.md +33 -0
  3. cluxion_agentplugin_adapters/codex/config-snippet.toml +5 -0
  4. cluxion_agentplugin_docs/cluxion-Docs/README.md +22 -0
  5. cluxion_agentplugin_docs/cluxion-Docs/architecture.md +36 -0
  6. cluxion_agentplugin_docs/cluxion-Docs/harness-logic.md +51 -0
  7. cluxion_agentplugin_docs/cluxion-Docs/honesty-preprocessing.md +40 -0
  8. cluxion_agentplugin_docs/cluxion-Docs/install-and-operations.md +36 -0
  9. cluxion_agentplugin_docs/cluxion-Docs/security.md +27 -0
  10. cluxion_agentplugin_docs/github-profile/README.md +67 -0
  11. cluxion_agentplugin_preprocessing/__init__.py +7 -0
  12. cluxion_agentplugin_preprocessing/cli.py +124 -0
  13. cluxion_agentplugin_preprocessing/hermes_config.py +163 -0
  14. cluxion_agentplugin_preprocessing/plugin.py +135 -0
  15. cluxion_agentplugin_preprocessing/plugin.yaml +13 -0
  16. cluxion_agentplugin_preprocessing/runner.py +241 -0
  17. cluxion_agentplugin_preprocessing/schemas.py +148 -0
  18. cluxion_agentplugin_preprocessing-0.2.0.dist-info/METADATA +115 -0
  19. cluxion_agentplugin_preprocessing-0.2.0.dist-info/RECORD +48 -0
  20. cluxion_agentplugin_preprocessing-0.2.0.dist-info/WHEEL +4 -0
  21. cluxion_agentplugin_preprocessing-0.2.0.dist-info/entry_points.txt +8 -0
  22. cluxion_agentplugin_preprocessing-0.2.0.dist-info/licenses/LICENSE +197 -0
  23. cluxion_runtime/__init__.py +16 -0
  24. cluxion_runtime/__main__.py +5 -0
  25. cluxion_runtime/adapters/__init__.py +25 -0
  26. cluxion_runtime/adapters/contract.py +82 -0
  27. cluxion_runtime/adapters/grok_build.py +35 -0
  28. cluxion_runtime/adapters/hermes.py +161 -0
  29. cluxion_runtime/adapters/spec.py +35 -0
  30. cluxion_runtime/bootstrap.py +270 -0
  31. cluxion_runtime/cli.py +282 -0
  32. cluxion_runtime/core/__init__.py +36 -0
  33. cluxion_runtime/core/clarification.py +192 -0
  34. cluxion_runtime/core/dispatch_store.py +270 -0
  35. cluxion_runtime/core/harness.py +320 -0
  36. cluxion_runtime/core/intent.py +55 -0
  37. cluxion_runtime/core/ledger.py +189 -0
  38. cluxion_runtime/core/ledger_codec.py +38 -0
  39. cluxion_runtime/core/plan_codec.py +121 -0
  40. cluxion_runtime/core/preprocess.py +497 -0
  41. cluxion_runtime/core/types.py +220 -0
  42. cluxion_runtime/core/work_queue.py +73 -0
  43. cluxion_runtime/models/__init__.py +15 -0
  44. cluxion_runtime/models/supervisor.py +156 -0
  45. cluxion_runtime/models/vllm_mlx.py +87 -0
  46. cluxion_runtime/resources/__init__.py +7 -0
  47. cluxion_runtime/resources/queue_bridge.py +128 -0
  48. 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"]