topoexploit 0.2.0__tar.gz → 0.3.0__tar.gz

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 (42) hide show
  1. {topoexploit-0.2.0 → topoexploit-0.3.0}/PKG-INFO +14 -1
  2. {topoexploit-0.2.0 → topoexploit-0.3.0}/README.md +13 -0
  3. {topoexploit-0.2.0 → topoexploit-0.3.0}/pyproject.toml +1 -1
  4. topoexploit-0.3.0/tests/test_checkpoint_meta.py +34 -0
  5. topoexploit-0.3.0/tests/test_data_pipeline.py +61 -0
  6. topoexploit-0.3.0/tests/test_eval_tasks.py +42 -0
  7. topoexploit-0.3.0/tests/test_fallback.py +133 -0
  8. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/PKG-INFO +14 -1
  9. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/SOURCES.txt +11 -0
  10. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/api_server.py +722 -283
  11. topoexploit-0.3.0/topogpt3/chat.py +196 -0
  12. topoexploit-0.3.0/topogpt3/checkpoint_meta.py +130 -0
  13. topoexploit-0.3.0/topogpt3/data_pipeline.py +325 -0
  14. topoexploit-0.3.0/topogpt3/eval_toolcall.py +98 -0
  15. topoexploit-0.3.0/topogpt3/fallback.py +509 -0
  16. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/inference_hrm.py +37 -35
  17. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/model.py +116 -93
  18. topoexploit-0.3.0/topogpt3/tokenizer_tools.py +216 -0
  19. topoexploit-0.3.0/topogpt3/tools_agent.py +185 -0
  20. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/train.py +5 -0
  21. {topoexploit-0.2.0 → topoexploit-0.3.0}/LICENSE +0 -0
  22. {topoexploit-0.2.0 → topoexploit-0.3.0}/setup.cfg +0 -0
  23. {topoexploit-0.2.0 → topoexploit-0.3.0}/tests/test_jlens.py +0 -0
  24. {topoexploit-0.2.0 → topoexploit-0.3.0}/tests/test_lens_model.py +0 -0
  25. {topoexploit-0.2.0 → topoexploit-0.3.0}/tests/test_structured_output.py +0 -0
  26. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/dependency_links.txt +0 -0
  27. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/entry_points.txt +0 -0
  28. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/requires.txt +0 -0
  29. {topoexploit-0.2.0 → topoexploit-0.3.0}/topoexploit.egg-info/top_level.txt +0 -0
  30. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/__init__.py +0 -0
  31. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/__main__.py +0 -0
  32. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/continuation.py +0 -0
  33. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/ewc.py +0 -0
  34. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/exploitgym_config.py +0 -0
  35. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/exploitgym_loader.py +0 -0
  36. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/hodge_cm.py +0 -0
  37. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/inference.py +0 -0
  38. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/jlens.py +0 -0
  39. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/lens_model.py +0 -0
  40. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/merged_config.py +0 -0
  41. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/structured_output.py +0 -0
  42. {topoexploit-0.2.0 → topoexploit-0.3.0}/topogpt3/vanilla_control.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topoexploit
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: TopoExploit: complex-valued spectral language model for security tasks (vulnerability analysis, patch analysis, exploit development), trained on ExploitGym.
5
5
  Author: grisun0
6
6
  License: GPL-3.0-or-later
@@ -282,6 +282,19 @@ Or install everything at once:
282
282
  pip install -e ".[train,lens,api,dev]"
283
283
  ```
284
284
 
285
+ ### Local fallback deployment (LazyOwn / TopoSwarm)
286
+
287
+ Both repos ship an OpenAI-compatible fallback contract (full chat history,
288
+ `tool` role, top-level `tools`, `best_of` reranking, `min_new_tokens`,
289
+ deterministic `seed`, apt_chain tool validation). See
290
+ [Fallback Integration](docs/FALLBACK_INTEGRATION.md); no code changes are
291
+ required in LazyOwn or TopoSwarm, they only need the `base_url`:
292
+
293
+ ```bash
294
+ bash scripts/serve_fallback.sh # TopoGPT3 :8800/v1 + TopoExploit :8801/v1
295
+ make eval-tasks # apt_chain JSONL task evals
296
+ ```
297
+
285
298
  ### Structured outputs and deterministic seeds
286
299
 
287
300
  The API server adopts the structured-output surface used by DeepInfer and
@@ -239,6 +239,19 @@ Or install everything at once:
239
239
  pip install -e ".[train,lens,api,dev]"
240
240
  ```
241
241
 
242
+ ### Local fallback deployment (LazyOwn / TopoSwarm)
243
+
244
+ Both repos ship an OpenAI-compatible fallback contract (full chat history,
245
+ `tool` role, top-level `tools`, `best_of` reranking, `min_new_tokens`,
246
+ deterministic `seed`, apt_chain tool validation). See
247
+ [Fallback Integration](docs/FALLBACK_INTEGRATION.md); no code changes are
248
+ required in LazyOwn or TopoSwarm, they only need the `base_url`:
249
+
250
+ ```bash
251
+ bash scripts/serve_fallback.sh # TopoGPT3 :8800/v1 + TopoExploit :8801/v1
252
+ make eval-tasks # apt_chain JSONL task evals
253
+ ```
254
+
242
255
  ### Structured outputs and deterministic seeds
243
256
 
244
257
  The API server adopts the structured-output surface used by DeepInfer and
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "topoexploit"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "TopoExploit: complex-valued spectral language model for security tasks (vulnerability analysis, patch analysis, exploit development), trained on ExploitGym."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,34 @@
1
+ """Checkpoint architecture discovery tests."""
2
+
3
+ import pytest
4
+
5
+ from topogpt3.checkpoint_meta import (
6
+ config_from_checkpoint,
7
+ probe_checkpoint,
8
+ read_state,
9
+ )
10
+
11
+
12
+ def test_read_state_missing(tmp_path):
13
+ assert read_state(tmp_path) == {}
14
+
15
+
16
+ def test_probe_t3_small():
17
+ cfg, meta = config_from_checkpoint("checkpoints_topogpt3/last", device="cpu")
18
+ assert meta["scale"] == "small"
19
+ assert cfg.D_MODEL == 256 and cfg.N_LAYERS == 6
20
+ assert cfg.N_KV_HEADS == 2
21
+ assert cfg.MAX_SEQ_LEN == 512
22
+
23
+
24
+ def test_probe_exploitgym_meta():
25
+ pytest.importorskip("safetensors")
26
+ meta = probe_checkpoint("checkpoints_topogpt3/last")
27
+ assert meta["vocab_size"] == 50257
28
+ assert meta["d_model"] == 256
29
+
30
+
31
+ def test_config_matches_weights():
32
+ cfg, meta = config_from_checkpoint("checkpoints_topogpt3/last", device="cpu")
33
+ assert cfg.D_MODEL == meta["d_model"]
34
+ assert cfg.N_LAYERS == meta["n_layers"]
@@ -0,0 +1,61 @@
1
+ """Data pipeline tests: dedup, filters, teacher/verify/export (offline)."""
2
+
3
+ import json
4
+
5
+ from topogpt3.data_pipeline import (
6
+ dedup_stream,
7
+ normalize_text,
8
+ quality_filter,
9
+ to_chat_row,
10
+ verify_completion,
11
+ write_jsonl,
12
+ )
13
+
14
+
15
+ def test_normalize_strips_control_and_collapses_space():
16
+ text = "a \x00b\t\t c\n\n\n\n\n\nd"
17
+ cleaned = normalize_text(text)
18
+ assert "\x00" not in cleaned
19
+ assert "b c" in cleaned
20
+ assert cleaned.count("\n\n") == 1
21
+
22
+
23
+ def test_dedup_exact_and_fuzzy():
24
+ rows = [
25
+ {"text": "hello world"},
26
+ {"text": "hello world"},
27
+ {"text": "different content here"},
28
+ ]
29
+ out = list(dedup_stream(rows, fuzzy=False))
30
+ assert len(out) == 2
31
+ out = list(dedup_stream(rows, fuzzy=True))
32
+ assert len(out) == 2
33
+
34
+
35
+ def test_quality_filter_bounds():
36
+ ok, reason = quality_filter("short")
37
+ assert not ok
38
+ ok, reason = quality_filter("x" * 600)
39
+ assert not ok and reason == "long_line"
40
+ ok, _reason = quality_filter("valid content with enough chars")
41
+ assert ok
42
+
43
+
44
+ def test_verify_python_ast_and_sandbox():
45
+ ok, _ = verify_completion("```python\nprint(1)\n```", mode="python_ast")
46
+ assert ok
47
+ ok, _reason = verify_completion("```python\ndef broken(:\n```", mode="python_ast")
48
+ assert not ok
49
+
50
+
51
+ def test_write_jsonl_roundtrip(tmp_path):
52
+ path = tmp_path / "out.jsonl"
53
+ n = write_jsonl(path, [{"a": 1}, {"a": 2}])
54
+ rows = [json.loads(line) for line in path.read_text().splitlines()]
55
+ assert n == 2 and rows[0]["a"] == 1
56
+
57
+
58
+ def test_to_chat_row():
59
+ row = to_chat_row({"prompt": "p", "completion": "c"})
60
+ assert row["messages"][0]["role"] == "user"
61
+ assert row["messages"][1]["role"] == "assistant"
@@ -0,0 +1,42 @@
1
+ """Generic task eval tests (no model load)."""
2
+
3
+ import json
4
+
5
+ from eval.tasks import (
6
+ evaluate_cases,
7
+ load_cases,
8
+ summary,
9
+ verify_regex,
10
+ verify_tool_call,
11
+ )
12
+ from topogpt3.chat import TOOL_CALL_CLOSE, TOOL_CALL_OPEN
13
+ from topogpt3.fallback import resolve_profile, synthesize_tool_call
14
+
15
+
16
+ def _gen(prompt, profile_name=""):
17
+ call = synthesize_tool_call(resolve_profile(profile_name), prompt)
18
+ return f"{TOOL_CALL_OPEN}{json.dumps(call)}{TOOL_CALL_CLOSE}" if call else ""
19
+
20
+
21
+ def test_apt_chain_cases_full_pass():
22
+ cases = load_cases("eval/apt_chain_cases.jsonl")
23
+ results = evaluate_cases(cases, _gen)
24
+ stats = summary(results)
25
+ assert stats["accuracy"] == 1.0, [
26
+ (r["id"], r["reason"]) for r in results if not r["ok"]
27
+ ]
28
+
29
+
30
+ def test_regex_verifier():
31
+ case = {"verifier": {"type": "regex", "pattern": r"[A-Z]{2}-[0-9]{3}"}}
32
+ assert verify_regex(case, "AB-123")[0]
33
+ assert not verify_regex(case, "ab-123")[0]
34
+
35
+
36
+ def test_tool_call_verifier_rejects_wrong_target():
37
+ case = {
38
+ "verifier": {"type": "apt_chain", "tool": "apt_chain", "target": "10.0.0.9"}
39
+ }
40
+ completion = _gen("Run the full chain against 10.10.11.78.", "apt-chain")
41
+ ok, reason = verify_tool_call(case, completion)
42
+ assert not ok and "target" in reason
@@ -0,0 +1,133 @@
1
+ """Fallback profiles, apt_chain validation and candidate reranking tests."""
2
+
3
+ import json
4
+
5
+ from topogpt3.chat import TOOL_CALL_CLOSE, TOOL_CALL_OPEN
6
+ from topogpt3.fallback import (
7
+ APT_CHAIN_TOOL,
8
+ LAZYOWN_TOOLS,
9
+ PROFILES,
10
+ rank_candidates,
11
+ resolve_profile,
12
+ score_candidate,
13
+ synthesize_tool_call,
14
+ tool_calls_payload,
15
+ validate_tool_call,
16
+ )
17
+
18
+
19
+ def _call(name, args):
20
+ return f"{TOOL_CALL_OPEN}{json.dumps({'name': name, 'arguments': args})}{TOOL_CALL_CLOSE}"
21
+
22
+
23
+ class TestProfiles:
24
+ def test_resolve_by_name_and_alias(self):
25
+ assert resolve_profile("topogpt3").name == "topogpt3"
26
+ assert resolve_profile("apt_chain").name == "topoexploit-apt-chain"
27
+ assert resolve_profile(None).name == "topogpt3"
28
+
29
+ def test_unknown_falls_back(self):
30
+ assert resolve_profile("nope-xyz").name == "topogpt3"
31
+
32
+ def test_apt_chain_tools_registered(self):
33
+ names = {t["function"]["name"] for t in LAZYOWN_TOOLS}
34
+ assert "apt_chain" in names
35
+ assert APT_CHAIN_TOOL["function"]["parameters"]["required"] == ["target"]
36
+
37
+
38
+ class TestValidateToolCall:
39
+ def test_valid_apt_chain(self):
40
+ ok, reason = validate_tool_call(
41
+ {
42
+ "name": "apt_chain",
43
+ "arguments": {"target": "10.10.11.78", "dry_run": True},
44
+ }
45
+ )
46
+ assert ok, reason
47
+
48
+ def test_requires_target(self):
49
+ ok, reason = validate_tool_call({"name": "apt_chain", "arguments": {}})
50
+ assert not ok and "target" in reason
51
+
52
+ def test_invalid_chain_name(self):
53
+ ok, _reason = validate_tool_call(
54
+ {
55
+ "name": "apt_chain",
56
+ "arguments": {"target": "10.0.0.1", "chain": "bad chain;"},
57
+ }
58
+ )
59
+ assert not ok
60
+
61
+ def test_run_command_forbidden_chars(self):
62
+ ok, _ = validate_tool_call(
63
+ {"name": "run_command", "arguments": {"command": "ls; rm -rf /"}}
64
+ )
65
+ assert not ok
66
+
67
+ def test_unknown_tool(self):
68
+ ok, reason = validate_tool_call({"name": "web_get", "arguments": {}})
69
+ assert not ok and "unknown" in reason
70
+
71
+
72
+ class TestScoring:
73
+ def test_tool_profile_prefers_valid_call(self):
74
+ profile = resolve_profile("apt-chain")
75
+ good = _call("apt_chain", {"target": "10.10.11.78", "dry_run": True})
76
+ prose = "I would run the chain for you."
77
+ best_text, score = "", -1e18
78
+ for text in (prose, good):
79
+ s, _d = score_candidate(text, profile)
80
+ if s > score:
81
+ best_text, score = text, s
82
+ assert best_text == good and score > 0
83
+
84
+ def test_require_tool_penalizes_prose(self):
85
+ profile = resolve_profile("apt-chain")
86
+ score, _ = score_candidate("Sure, tell me more.", profile)
87
+ assert score < 0
88
+
89
+ def test_code_profile_scores_parseable_code(self):
90
+ profile = resolve_profile("topogpt3-code")
91
+ good = "```python\ndef f(n):\n return n+1\n```"
92
+ bad = "```python\ndef f(n):\n return n+\n```"
93
+ assert score_candidate(good, profile)[0] > score_candidate(bad, profile)[0]
94
+
95
+ def test_rank_candidates_sorted(self):
96
+ profile = resolve_profile("apt-chain")
97
+ ranked = rank_candidates(
98
+ ["prose", _call("apt_chain", {"target": "10.0.0.1"})], profile
99
+ )
100
+ assert ranked[0][2].find("apt_chain") != -1
101
+
102
+
103
+ class TestSynthesize:
104
+ def test_ip_prompt(self):
105
+ profile = resolve_profile("apt-chain")
106
+ call = synthesize_tool_call(profile, "Run the full chain against 10.10.11.78.")
107
+ assert call and call["name"] == "apt_chain"
108
+ assert call["arguments"]["target"] == "10.10.11.78"
109
+ assert call["arguments"]["dry_run"] is True
110
+
111
+ def test_playbook_extraction(self):
112
+ call = synthesize_tool_call(
113
+ resolve_profile("apt-chain"),
114
+ "Emulate APT29 with chain apt_rocke against 10.0.0.5",
115
+ )
116
+ assert call and call["arguments"]["chain"] == "apt_rocke"
117
+
118
+ def test_no_target_no_call(self):
119
+ assert synthesize_tool_call(resolve_profile("apt-chain"), "hello world") is None
120
+
121
+
122
+ class TestToolCallsPayload:
123
+ def test_openai_shape(self):
124
+ calls = tool_calls_payload(_call("apt_chain", {"target": "10.0.0.1"}))
125
+ assert calls[0]["type"] == "function"
126
+ assert calls[0]["function"]["name"] == "apt_chain"
127
+ args = json.loads(calls[0]["function"]["arguments"])
128
+ assert args["target"] == "10.0.0.1"
129
+
130
+
131
+ def test_all_profiles_have_system():
132
+ for profile in PROFILES.values():
133
+ assert profile.system.strip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topoexploit
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: TopoExploit: complex-valued spectral language model for security tasks (vulnerability analysis, patch analysis, exploit development), trained on ExploitGym.
5
5
  Author: grisun0
6
6
  License: GPL-3.0-or-later
@@ -282,6 +282,19 @@ Or install everything at once:
282
282
  pip install -e ".[train,lens,api,dev]"
283
283
  ```
284
284
 
285
+ ### Local fallback deployment (LazyOwn / TopoSwarm)
286
+
287
+ Both repos ship an OpenAI-compatible fallback contract (full chat history,
288
+ `tool` role, top-level `tools`, `best_of` reranking, `min_new_tokens`,
289
+ deterministic `seed`, apt_chain tool validation). See
290
+ [Fallback Integration](docs/FALLBACK_INTEGRATION.md); no code changes are
291
+ required in LazyOwn or TopoSwarm, they only need the `base_url`:
292
+
293
+ ```bash
294
+ bash scripts/serve_fallback.sh # TopoGPT3 :8800/v1 + TopoExploit :8801/v1
295
+ make eval-tasks # apt_chain JSONL task evals
296
+ ```
297
+
285
298
  ### Structured outputs and deterministic seeds
286
299
 
287
300
  The API server adopts the structured-output surface used by DeepInfer and
@@ -1,6 +1,10 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
+ tests/test_checkpoint_meta.py
5
+ tests/test_data_pipeline.py
6
+ tests/test_eval_tasks.py
7
+ tests/test_fallback.py
4
8
  tests/test_jlens.py
5
9
  tests/test_lens_model.py
6
10
  tests/test_structured_output.py
@@ -13,10 +17,15 @@ topoexploit.egg-info/top_level.txt
13
17
  topogpt3/__init__.py
14
18
  topogpt3/__main__.py
15
19
  topogpt3/api_server.py
20
+ topogpt3/chat.py
21
+ topogpt3/checkpoint_meta.py
16
22
  topogpt3/continuation.py
23
+ topogpt3/data_pipeline.py
24
+ topogpt3/eval_toolcall.py
17
25
  topogpt3/ewc.py
18
26
  topogpt3/exploitgym_config.py
19
27
  topogpt3/exploitgym_loader.py
28
+ topogpt3/fallback.py
20
29
  topogpt3/hodge_cm.py
21
30
  topogpt3/inference.py
22
31
  topogpt3/inference_hrm.py
@@ -25,5 +34,7 @@ topogpt3/lens_model.py
25
34
  topogpt3/merged_config.py
26
35
  topogpt3/model.py
27
36
  topogpt3/structured_output.py
37
+ topogpt3/tokenizer_tools.py
38
+ topogpt3/tools_agent.py
28
39
  topogpt3/train.py
29
40
  topogpt3/vanilla_control.py