coding-cli-runtime 0.1.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.
- coding_cli_runtime/__init__.py +108 -0
- coding_cli_runtime/auth.py +55 -0
- coding_cli_runtime/codex_cli.py +95 -0
- coding_cli_runtime/contracts.py +72 -0
- coding_cli_runtime/copilot_reasoning_baseline.json +66 -0
- coding_cli_runtime/copilot_reasoning_logs.py +81 -0
- coding_cli_runtime/failure_classification.py +183 -0
- coding_cli_runtime/json_io.py +81 -0
- coding_cli_runtime/provider_controls.py +101 -0
- coding_cli_runtime/provider_specs.py +749 -0
- coding_cli_runtime/py.typed +1 -0
- coding_cli_runtime/reasoning.py +95 -0
- coding_cli_runtime/redaction.py +20 -0
- coding_cli_runtime/schema_validation.py +101 -0
- coding_cli_runtime/schemas/normalized_run_result.v1.json +37 -0
- coding_cli_runtime/schemas/reasoning_metadata.v1.json +14 -0
- coding_cli_runtime/session_execution.py +604 -0
- coding_cli_runtime/session_logs.py +129 -0
- coding_cli_runtime/subprocess_runner.py +346 -0
- coding_cli_runtime-0.1.0.dist-info/METADATA +179 -0
- coding_cli_runtime-0.1.0.dist-info/RECORD +24 -0
- coding_cli_runtime-0.1.0.dist-info/WHEEL +5 -0
- coding_cli_runtime-0.1.0.dist-info/licenses/LICENSE +21 -0
- coding_cli_runtime-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from importlib.resources import files
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_json_value(path: Path) -> Any:
|
|
11
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_json_object(path: Path) -> dict[str, Any]:
|
|
15
|
+
payload = load_json_value(path)
|
|
16
|
+
if not isinstance(payload, Mapping):
|
|
17
|
+
raise ValueError(f"Expected JSON object in {path}")
|
|
18
|
+
return dict(payload)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_optional_json_object(path: Path) -> dict[str, Any] | None:
|
|
22
|
+
if not path.exists():
|
|
23
|
+
return None
|
|
24
|
+
try:
|
|
25
|
+
payload = load_json_value(path)
|
|
26
|
+
except (OSError, ValueError):
|
|
27
|
+
return None
|
|
28
|
+
if not isinstance(payload, Mapping):
|
|
29
|
+
return None
|
|
30
|
+
return dict(payload)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _package_resource(*path_parts: str) -> Any:
|
|
34
|
+
package = __package__
|
|
35
|
+
if not package:
|
|
36
|
+
raise RuntimeError("Package resources are unavailable outside package context")
|
|
37
|
+
return files(package).joinpath(*path_parts)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def packaged_resource_exists(*path_parts: str) -> bool:
|
|
41
|
+
return bool(_package_resource(*path_parts).is_file())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_packaged_json_value(*path_parts: str) -> Any:
|
|
45
|
+
resource = _package_resource(*path_parts)
|
|
46
|
+
if not resource.is_file():
|
|
47
|
+
raise FileNotFoundError(f"packaged resource not found: {'/'.join(path_parts)}")
|
|
48
|
+
return json.loads(resource.read_text(encoding="utf-8"))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def load_packaged_json_object(*path_parts: str) -> dict[str, Any]:
|
|
52
|
+
payload = load_packaged_json_value(*path_parts)
|
|
53
|
+
if not isinstance(payload, Mapping):
|
|
54
|
+
raise ValueError(f"Expected JSON object in packaged resource {'/'.join(path_parts)}")
|
|
55
|
+
return dict(payload)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_jsonl_rows_text(text: str, *, skip_invalid: bool = False) -> list[dict[str, Any]]:
|
|
59
|
+
rows: list[dict[str, Any]] = []
|
|
60
|
+
for line_number, raw_line in enumerate(text.splitlines(), start=1):
|
|
61
|
+
payload = raw_line.strip()
|
|
62
|
+
if not payload:
|
|
63
|
+
continue
|
|
64
|
+
try:
|
|
65
|
+
decoded = json.loads(payload)
|
|
66
|
+
except json.JSONDecodeError as exc:
|
|
67
|
+
if skip_invalid:
|
|
68
|
+
continue
|
|
69
|
+
raise ValueError(f"Invalid JSON on line {line_number}") from exc
|
|
70
|
+
if not isinstance(decoded, Mapping):
|
|
71
|
+
if skip_invalid:
|
|
72
|
+
continue
|
|
73
|
+
raise ValueError(f"Expected JSON object on line {line_number}")
|
|
74
|
+
rows.append(dict(decoded))
|
|
75
|
+
return rows
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_jsonl_rows(path: Path, *, skip_invalid: bool = False) -> list[dict[str, Any]]:
|
|
79
|
+
if not path.exists():
|
|
80
|
+
return []
|
|
81
|
+
return load_jsonl_rows_text(path.read_text(encoding="utf-8"), skip_invalid=skip_invalid)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Provider model-control normalization helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
PROVIDER_SUPPORTED_MODEL_CONTROLS: dict[str, set[str]] = {
|
|
8
|
+
"codex": {"reasoning_effort"},
|
|
9
|
+
"claude": {"effort", "thinking_tokens"},
|
|
10
|
+
"copilot": {"reasoning_effort"},
|
|
11
|
+
"gemini": set(),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _normalize_reasoning_effort(value: Any) -> str | None:
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
normalized = value.strip()
|
|
18
|
+
if normalized:
|
|
19
|
+
return normalized
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _normalize_thinking_tokens(value: Any) -> int | None:
|
|
24
|
+
if isinstance(value, bool):
|
|
25
|
+
return None
|
|
26
|
+
if isinstance(value, int):
|
|
27
|
+
return max(int(value), 0)
|
|
28
|
+
if isinstance(value, str):
|
|
29
|
+
try:
|
|
30
|
+
parsed = int(value.strip())
|
|
31
|
+
except ValueError:
|
|
32
|
+
return None
|
|
33
|
+
return max(parsed, 0)
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _normalize_claude_effort(value: Any) -> str | None:
|
|
38
|
+
if not isinstance(value, str):
|
|
39
|
+
return None
|
|
40
|
+
normalized = value.strip().lower()
|
|
41
|
+
if normalized in {"low", "medium", "high"}:
|
|
42
|
+
return normalized
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def resolve_provider_model_controls(
|
|
47
|
+
*,
|
|
48
|
+
provider: str,
|
|
49
|
+
model_reasoning: str | None = None,
|
|
50
|
+
thinking_tokens: int | None = None,
|
|
51
|
+
effort: str | None = None,
|
|
52
|
+
model_controls: dict[str, Any] | None = None,
|
|
53
|
+
) -> dict[str, Any]:
|
|
54
|
+
provider_value = str(provider).strip().lower()
|
|
55
|
+
requested_raw: dict[str, Any] = {}
|
|
56
|
+
|
|
57
|
+
if isinstance(model_controls, dict):
|
|
58
|
+
for key, value in model_controls.items():
|
|
59
|
+
if isinstance(key, str):
|
|
60
|
+
requested_raw[key] = value
|
|
61
|
+
|
|
62
|
+
if "reasoning_effort" not in requested_raw and model_reasoning is not None:
|
|
63
|
+
requested_raw["reasoning_effort"] = model_reasoning
|
|
64
|
+
if "thinking_tokens" not in requested_raw and thinking_tokens is not None:
|
|
65
|
+
requested_raw["thinking_tokens"] = thinking_tokens
|
|
66
|
+
if "effort" not in requested_raw and effort is not None:
|
|
67
|
+
requested_raw["effort"] = effort
|
|
68
|
+
|
|
69
|
+
requested: dict[str, Any] = {}
|
|
70
|
+
reasoning_effort = _normalize_reasoning_effort(requested_raw.get("reasoning_effort"))
|
|
71
|
+
if reasoning_effort is not None:
|
|
72
|
+
requested["reasoning_effort"] = reasoning_effort
|
|
73
|
+
|
|
74
|
+
tokens_value = _normalize_thinking_tokens(requested_raw.get("thinking_tokens"))
|
|
75
|
+
if tokens_value is not None:
|
|
76
|
+
requested["thinking_tokens"] = tokens_value
|
|
77
|
+
|
|
78
|
+
effort_value = _normalize_claude_effort(requested_raw.get("effort"))
|
|
79
|
+
if effort_value is not None:
|
|
80
|
+
requested["effort"] = effort_value
|
|
81
|
+
|
|
82
|
+
supported = PROVIDER_SUPPORTED_MODEL_CONTROLS.get(provider_value, set())
|
|
83
|
+
applied = {key: requested[key] for key in sorted(requested) if key in supported}
|
|
84
|
+
ignored_keys = [key for key in sorted(requested) if key not in supported]
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
"provider": provider_value,
|
|
88
|
+
"requested": requested,
|
|
89
|
+
"applied": applied,
|
|
90
|
+
"ignored_keys": ignored_keys,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def build_model_id(model: str, *, applied_controls: dict[str, Any] | None = None) -> str:
|
|
95
|
+
controls = dict(applied_controls or {})
|
|
96
|
+
if not controls:
|
|
97
|
+
return model
|
|
98
|
+
if set(controls.keys()) == {"reasoning_effort"}:
|
|
99
|
+
return f"{model}:{controls['reasoning_effort']}"
|
|
100
|
+
suffix = ",".join(f"{key}={controls[key]}" for key in sorted(controls))
|
|
101
|
+
return f"{model}:{suffix}"
|