vantageai-agent 0.1.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 (48) hide show
  1. vantageai_agent-0.1.0/PKG-INFO +167 -0
  2. vantageai_agent-0.1.0/README.md +137 -0
  3. vantageai_agent-0.1.0/pyproject.toml +45 -0
  4. vantageai_agent-0.1.0/setup.cfg +4 -0
  5. vantageai_agent-0.1.0/tests/test_anomaly.py +31 -0
  6. vantageai_agent-0.1.0/tests/test_backends.py +89 -0
  7. vantageai_agent-0.1.0/tests/test_bugs.py +127 -0
  8. vantageai_agent-0.1.0/tests/test_classifier.py +211 -0
  9. vantageai_agent-0.1.0/tests/test_cost_tracker.py +131 -0
  10. vantageai_agent-0.1.0/tests/test_hooks.py +93 -0
  11. vantageai_agent-0.1.0/tests/test_integration.py +634 -0
  12. vantageai_agent-0.1.0/tests/test_optimizer.py +152 -0
  13. vantageai_agent-0.1.0/tests/test_permissions.py +146 -0
  14. vantageai_agent-0.1.0/tests/test_pricing.py +123 -0
  15. vantageai_agent-0.1.0/tests/test_recommendations.py +248 -0
  16. vantageai_agent-0.1.0/tests/test_rendering.py +562 -0
  17. vantageai_agent-0.1.0/tests/test_session.py +149 -0
  18. vantageai_agent-0.1.0/tests/test_tools.py +251 -0
  19. vantageai_agent-0.1.0/tests/test_tracker.py +88 -0
  20. vantageai_agent-0.1.0/vantage_agent/__init__.py +2 -0
  21. vantageai_agent-0.1.0/vantage_agent/anomaly.py +61 -0
  22. vantageai_agent-0.1.0/vantage_agent/api_client.py +267 -0
  23. vantageai_agent-0.1.0/vantage_agent/backends/__init__.py +52 -0
  24. vantageai_agent-0.1.0/vantage_agent/backends/api_backend.py +48 -0
  25. vantageai_agent-0.1.0/vantage_agent/backends/base.py +83 -0
  26. vantageai_agent-0.1.0/vantage_agent/backends/claude_backend.py +63 -0
  27. vantageai_agent-0.1.0/vantage_agent/backends/codex_backend.py +63 -0
  28. vantageai_agent-0.1.0/vantage_agent/backends/gemini_backend.py +63 -0
  29. vantageai_agent-0.1.0/vantage_agent/classifier.py +136 -0
  30. vantageai_agent-0.1.0/vantage_agent/cli.py +339 -0
  31. vantageai_agent-0.1.0/vantage_agent/cost_tracker.py +66 -0
  32. vantageai_agent-0.1.0/vantage_agent/hooks.py +131 -0
  33. vantageai_agent-0.1.0/vantage_agent/optimizer.py +232 -0
  34. vantageai_agent-0.1.0/vantage_agent/permissions.py +140 -0
  35. vantageai_agent-0.1.0/vantage_agent/pricing.py +117 -0
  36. vantageai_agent-0.1.0/vantage_agent/recommendations.py +532 -0
  37. vantageai_agent-0.1.0/vantage_agent/renderer.py +146 -0
  38. vantageai_agent-0.1.0/vantage_agent/session.py +198 -0
  39. vantageai_agent-0.1.0/vantage_agent/session_store.py +51 -0
  40. vantageai_agent-0.1.0/vantage_agent/tool_registry.py +42 -0
  41. vantageai_agent-0.1.0/vantage_agent/tools.py +297 -0
  42. vantageai_agent-0.1.0/vantage_agent/tracker.py +189 -0
  43. vantageai_agent-0.1.0/vantageai_agent.egg-info/PKG-INFO +167 -0
  44. vantageai_agent-0.1.0/vantageai_agent.egg-info/SOURCES.txt +46 -0
  45. vantageai_agent-0.1.0/vantageai_agent.egg-info/dependency_links.txt +1 -0
  46. vantageai_agent-0.1.0/vantageai_agent.egg-info/entry_points.txt +2 -0
  47. vantageai_agent-0.1.0/vantageai_agent.egg-info/requires.txt +6 -0
  48. vantageai_agent-0.1.0/vantageai_agent.egg-info/top_level.txt +1 -0
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: vantageai-agent
3
+ Version: 0.1.0
4
+ Summary: AI coding agent with per-tool permissions, cost tracking, and token optimization
5
+ Author-email: Aman Jain <aman@vantageaiops.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://vantageaiops.com
8
+ Project-URL: Documentation, https://vantageaiops.com/docs
9
+ Project-URL: Repository, https://github.com/amanjain/vantageai
10
+ Project-URL: Bug Tracker, https://github.com/amanjain/vantageai/issues
11
+ Keywords: ai,agent,cost-tracking,claude,codex,gemini,llm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ Requires-Dist: anthropic>=0.40.0
26
+ Requires-Dist: rich>=13.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
30
+
31
+ # Vantage Agent
32
+
33
+ AI coding agent with per-tool permissions, cost tracking, prompt optimization, and anomaly detection. Works across Claude (API), Claude Code, Codex CLI, and Gemini CLI.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install vantageai-agent
39
+ ```
40
+
41
+ Requires Python 3.9+.
42
+
43
+ ## Quick Start
44
+
45
+ ### Interactive REPL
46
+
47
+ ```bash
48
+ # Set your API key, then launch
49
+ export ANTHROPIC_API_KEY=sk-...
50
+ vantageai-agent
51
+ ```
52
+
53
+ ### One-shot prompt
54
+
55
+ ```bash
56
+ vantageai-agent "explain the main function in cli.py"
57
+ ```
58
+
59
+ ### Pipe mode
60
+
61
+ ```bash
62
+ echo "summarize this file" | vantageai-agent
63
+ ```
64
+
65
+ ## Backends
66
+
67
+ Vantage auto-detects the best backend. You can override with `--backend`:
68
+
69
+ | Backend | Description | Requires |
70
+ |---------|-------------|---------|
71
+ | `api` | Direct Anthropic API (exact token counts) | `ANTHROPIC_API_KEY` |
72
+ | `claude` | Claude Code subprocess (free tier / subscription) | `claude` binary |
73
+ | `codex` | OpenAI Codex CLI subprocess | `codex` binary |
74
+ | `gemini` | Gemini CLI subprocess | `gemini` binary |
75
+
76
+ ```bash
77
+ vantageai-agent --backend claude "refactor this function"
78
+ vantageai-agent --backend codex "write unit tests for auth.py"
79
+ ```
80
+
81
+ Auto-detect priority: `VANTAGE_BACKEND` env → `ANTHROPIC_API_KEY` → `claude` binary → `codex` binary → `gemini` binary.
82
+
83
+ ## Session Management
84
+
85
+ Sessions are persisted to `~/.vantage/sessions/`. Resume any previous session:
86
+
87
+ ```bash
88
+ # List sessions and costs
89
+ vantageai-agent summary
90
+
91
+ # Resume by session ID
92
+ vantageai-agent --resume abc12345
93
+ ```
94
+
95
+ ## REPL Commands
96
+
97
+ | Command | Description |
98
+ |---------|-------------|
99
+ | `/help` | Show all commands |
100
+ | `/tools` | Show tool approval status |
101
+ | `/allow Bash,Write` | Approve specific tools |
102
+ | `/allow all` | Approve all tools for this session |
103
+ | `/cost` | Show current session cost |
104
+ | `/optimize on\|off` | Toggle prompt optimization |
105
+ | `/model claude-opus-4-6` | Switch model mid-session |
106
+ | `/reset` | Clear history, permissions, cost |
107
+ | `/quit` | Exit (shows final cost summary) |
108
+
109
+ ## Cost Tracking
110
+
111
+ Every turn shows token usage and cost. Exit shows a full session summary:
112
+
113
+ ```
114
+ +----- Cost Summary -----+
115
+ Model: claude-sonnet-4-6
116
+ Input tokens: 1,234
117
+ Output tokens: 456
118
+ Cost: $0.0123
119
+ Session total: $0.0456
120
+ Prompts: 3
121
+ +-------------------------+
122
+ ```
123
+
124
+ For Claude Code / Codex / Gemini (subscription or free tier), costs are labeled `~$0.00 (free tier)` or `~$0.0123 (estimated)`.
125
+
126
+ ## VantageAI Dashboard
127
+
128
+ Send telemetry to your VantageAI dashboard for cross-session cost analysis:
129
+
130
+ ```bash
131
+ export VANTAGE_API_KEY=vnt_...
132
+ vantageai-agent
133
+ ```
134
+
135
+ Or pass inline: `vantageai-agent --vantage-key vnt_...`
136
+
137
+ ## All Flags
138
+
139
+ ```
140
+ vantageai-agent [OPTIONS] [PROMPT]
141
+
142
+ PROMPT One-shot prompt (omit for interactive REPL)
143
+
144
+ --backend api | claude | codex | gemini (auto-detected)
145
+ --model Model ID (default: claude-sonnet-4-6)
146
+ --max-tokens Max output tokens (default: 16384)
147
+ --resume SESSION_ID Resume a previous session
148
+ --api-key Anthropic API key (or ANTHROPIC_API_KEY env)
149
+ --vantage-key VantageAI dashboard API key (or VANTAGE_API_KEY env)
150
+ --system Custom system prompt
151
+ --no-optimize Disable prompt optimization
152
+ --cwd Set working directory
153
+ --debug Enable debug output
154
+ ```
155
+
156
+ ## Environment Variables
157
+
158
+ | Variable | Description |
159
+ |----------|-------------|
160
+ | `ANTHROPIC_API_KEY` | Anthropic API key |
161
+ | `VANTAGE_API_KEY` | VantageAI dashboard key |
162
+ | `VANTAGE_BACKEND` | Force backend (api/claude/codex/gemini) |
163
+ | `VANTAGE_MODEL` | Default model override |
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,137 @@
1
+ # Vantage Agent
2
+
3
+ AI coding agent with per-tool permissions, cost tracking, prompt optimization, and anomaly detection. Works across Claude (API), Claude Code, Codex CLI, and Gemini CLI.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install vantageai-agent
9
+ ```
10
+
11
+ Requires Python 3.9+.
12
+
13
+ ## Quick Start
14
+
15
+ ### Interactive REPL
16
+
17
+ ```bash
18
+ # Set your API key, then launch
19
+ export ANTHROPIC_API_KEY=sk-...
20
+ vantageai-agent
21
+ ```
22
+
23
+ ### One-shot prompt
24
+
25
+ ```bash
26
+ vantageai-agent "explain the main function in cli.py"
27
+ ```
28
+
29
+ ### Pipe mode
30
+
31
+ ```bash
32
+ echo "summarize this file" | vantageai-agent
33
+ ```
34
+
35
+ ## Backends
36
+
37
+ Vantage auto-detects the best backend. You can override with `--backend`:
38
+
39
+ | Backend | Description | Requires |
40
+ |---------|-------------|---------|
41
+ | `api` | Direct Anthropic API (exact token counts) | `ANTHROPIC_API_KEY` |
42
+ | `claude` | Claude Code subprocess (free tier / subscription) | `claude` binary |
43
+ | `codex` | OpenAI Codex CLI subprocess | `codex` binary |
44
+ | `gemini` | Gemini CLI subprocess | `gemini` binary |
45
+
46
+ ```bash
47
+ vantageai-agent --backend claude "refactor this function"
48
+ vantageai-agent --backend codex "write unit tests for auth.py"
49
+ ```
50
+
51
+ Auto-detect priority: `VANTAGE_BACKEND` env → `ANTHROPIC_API_KEY` → `claude` binary → `codex` binary → `gemini` binary.
52
+
53
+ ## Session Management
54
+
55
+ Sessions are persisted to `~/.vantage/sessions/`. Resume any previous session:
56
+
57
+ ```bash
58
+ # List sessions and costs
59
+ vantageai-agent summary
60
+
61
+ # Resume by session ID
62
+ vantageai-agent --resume abc12345
63
+ ```
64
+
65
+ ## REPL Commands
66
+
67
+ | Command | Description |
68
+ |---------|-------------|
69
+ | `/help` | Show all commands |
70
+ | `/tools` | Show tool approval status |
71
+ | `/allow Bash,Write` | Approve specific tools |
72
+ | `/allow all` | Approve all tools for this session |
73
+ | `/cost` | Show current session cost |
74
+ | `/optimize on\|off` | Toggle prompt optimization |
75
+ | `/model claude-opus-4-6` | Switch model mid-session |
76
+ | `/reset` | Clear history, permissions, cost |
77
+ | `/quit` | Exit (shows final cost summary) |
78
+
79
+ ## Cost Tracking
80
+
81
+ Every turn shows token usage and cost. Exit shows a full session summary:
82
+
83
+ ```
84
+ +----- Cost Summary -----+
85
+ Model: claude-sonnet-4-6
86
+ Input tokens: 1,234
87
+ Output tokens: 456
88
+ Cost: $0.0123
89
+ Session total: $0.0456
90
+ Prompts: 3
91
+ +-------------------------+
92
+ ```
93
+
94
+ For Claude Code / Codex / Gemini (subscription or free tier), costs are labeled `~$0.00 (free tier)` or `~$0.0123 (estimated)`.
95
+
96
+ ## VantageAI Dashboard
97
+
98
+ Send telemetry to your VantageAI dashboard for cross-session cost analysis:
99
+
100
+ ```bash
101
+ export VANTAGE_API_KEY=vnt_...
102
+ vantageai-agent
103
+ ```
104
+
105
+ Or pass inline: `vantageai-agent --vantage-key vnt_...`
106
+
107
+ ## All Flags
108
+
109
+ ```
110
+ vantageai-agent [OPTIONS] [PROMPT]
111
+
112
+ PROMPT One-shot prompt (omit for interactive REPL)
113
+
114
+ --backend api | claude | codex | gemini (auto-detected)
115
+ --model Model ID (default: claude-sonnet-4-6)
116
+ --max-tokens Max output tokens (default: 16384)
117
+ --resume SESSION_ID Resume a previous session
118
+ --api-key Anthropic API key (or ANTHROPIC_API_KEY env)
119
+ --vantage-key VantageAI dashboard API key (or VANTAGE_API_KEY env)
120
+ --system Custom system prompt
121
+ --no-optimize Disable prompt optimization
122
+ --cwd Set working directory
123
+ --debug Enable debug output
124
+ ```
125
+
126
+ ## Environment Variables
127
+
128
+ | Variable | Description |
129
+ |----------|-------------|
130
+ | `ANTHROPIC_API_KEY` | Anthropic API key |
131
+ | `VANTAGE_API_KEY` | VantageAI dashboard key |
132
+ | `VANTAGE_BACKEND` | Force backend (api/claude/codex/gemini) |
133
+ | `VANTAGE_MODEL` | Default model override |
134
+
135
+ ## License
136
+
137
+ MIT
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vantageai-agent"
7
+ version = "0.1.0"
8
+ description = "AI coding agent with per-tool permissions, cost tracking, and token optimization"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "Aman Jain", email = "aman@vantageaiops.com" }]
13
+ keywords = ["ai", "agent", "cost-tracking", "claude", "codex", "gemini", "llm"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Topic :: Utilities",
26
+ ]
27
+ dependencies = [
28
+ "anthropic>=0.40.0",
29
+ "rich>=13.0.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://vantageaiops.com"
34
+ Documentation = "https://vantageaiops.com/docs"
35
+ Repository = "https://github.com/amanjain/vantageai"
36
+ "Bug Tracker" = "https://github.com/amanjain/vantageai/issues"
37
+
38
+ [project.optional-dependencies]
39
+ dev = ["pytest>=7.0", "pytest-asyncio>=0.21"]
40
+
41
+ [project.scripts]
42
+ vantageai-agent = "vantage_agent.cli:main"
43
+
44
+ [tool.setuptools.packages.find]
45
+ include = ["vantage_agent*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ """Tests for anomaly.py — cost anomaly detection."""
2
+ import pytest
3
+ from vantage_agent.anomaly import check_cost_anomaly
4
+
5
+
6
+ class TestCheckCostAnomaly:
7
+ def test_no_anomaly_with_few_priors(self):
8
+ assert check_cost_anomaly(0.10, 0.05, 1) is False
9
+
10
+ def test_no_anomaly_zero_prior(self):
11
+ assert check_cost_anomaly(0.10, 0.0, 5) is False
12
+
13
+ def test_no_anomaly_normal_cost(self):
14
+ # avg = 0.10/5 = 0.02, current 0.03 < 0.06 (3x)
15
+ assert check_cost_anomaly(0.03, 0.10, 5) is False
16
+
17
+ def test_anomaly_detected(self):
18
+ # avg = 0.10/5 = 0.02, current 0.10 > 0.06 (3x)
19
+ assert check_cost_anomaly(0.10, 0.10, 5) is True
20
+
21
+ def test_no_anomaly_below_min_avg(self):
22
+ # avg = 0.001/5 = 0.0002 < MIN_AVG_COST
23
+ assert check_cost_anomaly(0.01, 0.001, 5) is False
24
+
25
+ def test_exactly_3x_not_anomaly(self):
26
+ # avg = 0.10/5 = 0.02, current 0.06 == 3x (not >)
27
+ assert check_cost_anomaly(0.06, 0.10, 5) is False
28
+
29
+ def test_just_over_3x_is_anomaly(self):
30
+ # avg = 0.10/5 = 0.02, current 0.0601 > 0.06
31
+ assert check_cost_anomaly(0.0601, 0.10, 5) is True
@@ -0,0 +1,89 @@
1
+ """Tests for Backend implementations, auto-detect logic, and ToolRegistry."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ from unittest.mock import patch
6
+
7
+ import pytest
8
+
9
+ from vantage_agent.backends import create_backend, auto_detect_backend
10
+ from vantage_agent.backends.base import BackendCapabilities, BackendResult
11
+
12
+
13
+ def test_auto_detect_api_key_returns_api():
14
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "sk-test", "VANTAGE_BACKEND": ""}):
15
+ name = auto_detect_backend()
16
+ assert name == "api"
17
+
18
+
19
+ def test_auto_detect_claude_binary_returns_claude():
20
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "", "VANTAGE_BACKEND": ""}):
21
+ with patch("shutil.which", side_effect=lambda b: "/usr/bin/claude" if b == "claude" else None):
22
+ name = auto_detect_backend()
23
+ assert name == "claude"
24
+
25
+
26
+ def test_auto_detect_no_binaries_raises_error():
27
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "", "VANTAGE_BACKEND": ""}):
28
+ with patch("shutil.which", return_value=None):
29
+ with pytest.raises(RuntimeError, match="No backend found"):
30
+ auto_detect_backend()
31
+
32
+
33
+ def test_explicit_env_var_overrides_autodetect():
34
+ with patch.dict(os.environ, {"VANTAGE_BACKEND": "gemini", "ANTHROPIC_API_KEY": "sk-test"}):
35
+ name = auto_detect_backend()
36
+ assert name == "gemini"
37
+
38
+
39
+ def test_backend_capabilities_declared():
40
+ """Every backend must declare all 4 capability fields."""
41
+ for name in ("api", "claude", "codex", "gemini"):
42
+ backend = create_backend(name)
43
+ caps = backend.capabilities
44
+ assert isinstance(caps.supports_process, bool)
45
+ assert caps.token_count in ("exact", "estimated", "free_tier")
46
+ assert caps.tool_format in ("anthropic", "openai", "google")
47
+
48
+
49
+ def test_api_backend_not_supports_process():
50
+ backend = create_backend("api")
51
+ assert backend.capabilities.supports_process is False
52
+ assert backend.capabilities.token_count == "exact"
53
+
54
+
55
+ def test_cli_backends_support_process():
56
+ for name in ("claude", "codex", "gemini"):
57
+ backend = create_backend(name)
58
+ assert backend.capabilities.supports_process is True
59
+ assert backend.capabilities.token_count == "estimated"
60
+
61
+
62
+ def test_unknown_backend_raises():
63
+ with pytest.raises(ValueError, match="Unknown backend"):
64
+ create_backend("llama")
65
+
66
+
67
+ def test_tool_registry_translates_anthropic_to_openai():
68
+ from vantage_agent.tool_registry import ToolRegistry
69
+ tools = ToolRegistry.for_format("openai")
70
+ assert len(tools) > 0
71
+ for tool in tools:
72
+ assert "function" in tool, f"OpenAI format missing 'function' key: {tool}"
73
+ assert tool["type"] == "function"
74
+
75
+
76
+ def test_tool_registry_translates_anthropic_to_google():
77
+ from vantage_agent.tool_registry import ToolRegistry
78
+ tools = ToolRegistry.for_format("google")
79
+ assert len(tools) > 0
80
+ for tool in tools:
81
+ assert "name" in tool
82
+ assert "description" in tool
83
+
84
+
85
+ def test_tool_registry_anthropic_passthrough():
86
+ from vantage_agent.tool_registry import ToolRegistry
87
+ from vantage_agent.tools import TOOL_DEFINITIONS
88
+ tools = ToolRegistry.for_format("anthropic")
89
+ assert tools == list(TOOL_DEFINITIONS)
@@ -0,0 +1,127 @@
1
+ """Regression tests for 5 pre-existing bugs."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ from unittest.mock import patch
6
+
7
+ import httpx
8
+ import pytest
9
+
10
+ from vantage_agent.optimizer import optimize_prompt
11
+ from vantage_agent.tracker import Tracker, TrackerConfig
12
+
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # Bug 1: Semantic corruption — "whether or not"
16
+ # ---------------------------------------------------------------------------
17
+
18
+ def test_whether_or_not_preserves_question_intent():
19
+ """'whether or not' must NOT be stripped — changes question to directive."""
20
+ original = "Tell me whether or not to delete the file"
21
+ result = optimize_prompt(original)
22
+ optimized = result.optimized if hasattr(result, "optimized") else str(result)
23
+ assert "whether" in optimized.lower(), (
24
+ f"'whether or not' was incorrectly stripped. Got: {optimized!r}"
25
+ )
26
+
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Bug 2: anonymized privacy mode not implemented
30
+ # ---------------------------------------------------------------------------
31
+
32
+ def _make_tracker(privacy: str) -> Tracker:
33
+ cfg = TrackerConfig(api_key="test-key", privacy=privacy)
34
+ return Tracker(cfg)
35
+
36
+
37
+ def test_anonymized_mode_strips_agent_name_and_hashes_event_id():
38
+ """anonymized mode must strip agent_name/team and hash event_id."""
39
+ tracker = _make_tracker("anonymized")
40
+ tracker.record(
41
+ model="claude-sonnet-4-6",
42
+ input_tokens=100,
43
+ output_tokens=50,
44
+ cost_usd=0.001,
45
+ latency_ms=200,
46
+ agent_name="my-team-agent",
47
+ )
48
+ with tracker._lock:
49
+ event = tracker._queue[0]
50
+
51
+ assert event.agent_name == "", (
52
+ f"agent_name not stripped in anonymized mode: {event.agent_name!r}"
53
+ )
54
+ assert event.team == "", (
55
+ f"team not stripped in anonymized mode: {event.team!r}"
56
+ )
57
+ assert len(event.event_id) == 64 and all(
58
+ c in "0123456789abcdef" for c in event.event_id
59
+ ), f"event_id not hashed in anonymized mode: {event.event_id!r}"
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Bug 3: Unknown agent defaults to "anthropic" provider
64
+ # ---------------------------------------------------------------------------
65
+
66
+ def test_unknown_agent_provider_defaults_to_unknown_not_anthropic():
67
+ """Unrecognised agent_name must map to 'unknown', not 'anthropic'."""
68
+ tracker = _make_tracker("full")
69
+ tracker.record(
70
+ model="gpt-4o",
71
+ input_tokens=100,
72
+ output_tokens=50,
73
+ cost_usd=0.002,
74
+ latency_ms=300,
75
+ agent_name="some-random-gpt-tool",
76
+ )
77
+ with tracker._lock:
78
+ event = tracker._queue[0]
79
+ assert event.provider == "unknown", (
80
+ f"Unknown agent mapped to provider {event.provider!r} instead of 'unknown'"
81
+ )
82
+
83
+
84
+ # ---------------------------------------------------------------------------
85
+ # Bug 4: Pricing dictionaries inconsistent
86
+ # ---------------------------------------------------------------------------
87
+
88
+ def test_pricing_dictionaries_consistent():
89
+ """cost_tracker.py and pricing.py must use the same model key for claude-haiku-4-5."""
90
+ from vantage_agent.pricing import MODEL_PRICES
91
+ from vantage_agent.cost_tracker import MODEL_PRICING
92
+
93
+ haiku_key_in_pricing = any("haiku-4-5" in k for k in MODEL_PRICES)
94
+ haiku_key_in_tracker = any("haiku-4-5" in k for k in MODEL_PRICING)
95
+ assert haiku_key_in_pricing, "claude-haiku-4-5 not found in pricing.MODEL_PRICES"
96
+ assert haiku_key_in_tracker, "claude-haiku-4-5 not found in cost_tracker.MODEL_PRICING"
97
+
98
+ pricing_key = next(k for k in MODEL_PRICES if "haiku-4-5" in k)
99
+ tracker_key = next(k for k in MODEL_PRICING if "haiku-4-5" in k)
100
+ assert pricing_key == tracker_key, (
101
+ f"Key mismatch: pricing.py uses {pricing_key!r}, cost_tracker.py uses {tracker_key!r}"
102
+ )
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Bug 5: Flush-before-success loses events
107
+ # ---------------------------------------------------------------------------
108
+
109
+ def test_flush_retains_events_on_network_error():
110
+ """Events must NOT be cleared from queue if the HTTP POST fails."""
111
+ tracker = _make_tracker("full")
112
+ tracker.record(
113
+ model="claude-sonnet-4-6",
114
+ input_tokens=100,
115
+ output_tokens=50,
116
+ cost_usd=0.001,
117
+ latency_ms=100,
118
+ agent_name="vantage-agent",
119
+ )
120
+ assert len(tracker._queue) == 1
121
+
122
+ with patch("httpx.post", side_effect=httpx.ConnectError("timeout")):
123
+ tracker.flush()
124
+
125
+ assert len(tracker._queue) == 1, (
126
+ "Events were lost from queue after a failed HTTP POST"
127
+ )