bridgekit 0.3.7__tar.gz → 0.3.8__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 (26) hide show
  1. {bridgekit-0.3.7 → bridgekit-0.3.8}/PKG-INFO +12 -3
  2. {bridgekit-0.3.7 → bridgekit-0.3.8}/README.md +11 -2
  3. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/__init__.py +1 -1
  4. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/cli.py +1 -1
  5. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/config.py +1 -1
  6. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/planner.py +3 -2
  7. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/redteam.py +3 -2
  8. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/reviewer.py +3 -2
  9. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/search.py +3 -2
  10. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/PKG-INFO +12 -3
  11. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/SOURCES.txt +1 -0
  12. {bridgekit-0.3.7 → bridgekit-0.3.8}/pyproject.toml +1 -1
  13. bridgekit-0.3.8/tests/test_cli.py +175 -0
  14. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_planner.py +30 -0
  15. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_redteam.py +30 -0
  16. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_reviewer.py +30 -0
  17. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_search.py +42 -0
  18. {bridgekit-0.3.7 → bridgekit-0.3.8}/LICENSE +0 -0
  19. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit/providers.py +0 -0
  20. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/dependency_links.txt +0 -0
  21. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/entry_points.txt +0 -0
  22. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/requires.txt +0 -0
  23. {bridgekit-0.3.7 → bridgekit-0.3.8}/bridgekit.egg-info/top_level.txt +0 -0
  24. {bridgekit-0.3.7 → bridgekit-0.3.8}/setup.cfg +0 -0
  25. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_config.py +0 -0
  26. {bridgekit-0.3.7 → bridgekit-0.3.8}/tests/test_providers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bridgekit
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: AI tools that make you a better data scientist, not a redundant one.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://usebridgekit.com
@@ -136,6 +136,9 @@ onboarding users to reporting as a growth lever.
136
136
  """
137
137
 
138
138
  print(evaluate(text))
139
+
140
+ # Override for longer analyses
141
+ print(evaluate(text, max_tokens=2048))
139
142
  ```
140
143
 
141
144
  **Output:**
@@ -190,6 +193,9 @@ Supports `.txt`, `.md`, `.pdf`, `.docx`, `.pptx`, and `.ipynb` files.
190
193
  from bridgekit import ask
191
194
 
192
195
  print(ask("what drove churn in Q3?", source="reports/"))
196
+
197
+ # Override for longer responses
198
+ print(ask("what drove churn in Q3?", source="reports/", max_tokens=2048))
193
199
  ```
194
200
 
195
201
  **From raw text:**
@@ -234,7 +240,7 @@ print(plan(
234
240
  ))
235
241
  ```
236
242
 
237
- `data_description` and `goal` are optional — the more context you provide, the more tailored the recommendation.
243
+ `data_description`, `goal`, and `max_tokens` are optional — the more context you provide, the more tailored the recommendation.
238
244
 
239
245
  **`goal` examples:** `"causal inference"`, `"prediction"`, `"segmentation"`, `"hypothesis testing"`, `"exploration"`
240
246
 
@@ -297,6 +303,9 @@ print(redteam(text))
297
303
  # Or specify a stakeholder
298
304
  print(redteam(text, stakeholder="VP of Engineering"))
299
305
  print(redteam(text, stakeholder="VP of Marketing"))
306
+
307
+ # Override for longer responses
308
+ print(redteam(text, max_tokens=2048))
300
309
  ```
301
310
 
302
311
  Same writeup, different attack angles:
@@ -414,7 +423,7 @@ Bridgekit automatically detects the provider from model names:
414
423
  - Models starting with "gemini" → Google Gemini
415
424
 
416
425
  **Default models by provider:**
417
- - Anthropic: `claude-3-5-sonnet-20241022`
426
+ - Anthropic: `claude-opus-4-8`
418
427
  - OpenAI: `gpt-4o`
419
428
  - Gemini: `gemini-1.5-pro`
420
429
 
@@ -104,6 +104,9 @@ onboarding users to reporting as a growth lever.
104
104
  """
105
105
 
106
106
  print(evaluate(text))
107
+
108
+ # Override for longer analyses
109
+ print(evaluate(text, max_tokens=2048))
107
110
  ```
108
111
 
109
112
  **Output:**
@@ -158,6 +161,9 @@ Supports `.txt`, `.md`, `.pdf`, `.docx`, `.pptx`, and `.ipynb` files.
158
161
  from bridgekit import ask
159
162
 
160
163
  print(ask("what drove churn in Q3?", source="reports/"))
164
+
165
+ # Override for longer responses
166
+ print(ask("what drove churn in Q3?", source="reports/", max_tokens=2048))
161
167
  ```
162
168
 
163
169
  **From raw text:**
@@ -202,7 +208,7 @@ print(plan(
202
208
  ))
203
209
  ```
204
210
 
205
- `data_description` and `goal` are optional — the more context you provide, the more tailored the recommendation.
211
+ `data_description`, `goal`, and `max_tokens` are optional — the more context you provide, the more tailored the recommendation.
206
212
 
207
213
  **`goal` examples:** `"causal inference"`, `"prediction"`, `"segmentation"`, `"hypothesis testing"`, `"exploration"`
208
214
 
@@ -265,6 +271,9 @@ print(redteam(text))
265
271
  # Or specify a stakeholder
266
272
  print(redteam(text, stakeholder="VP of Engineering"))
267
273
  print(redteam(text, stakeholder="VP of Marketing"))
274
+
275
+ # Override for longer responses
276
+ print(redteam(text, max_tokens=2048))
268
277
  ```
269
278
 
270
279
  Same writeup, different attack angles:
@@ -382,7 +391,7 @@ Bridgekit automatically detects the provider from model names:
382
391
  - Models starting with "gemini" → Google Gemini
383
392
 
384
393
  **Default models by provider:**
385
- - Anthropic: `claude-3-5-sonnet-20241022`
394
+ - Anthropic: `claude-opus-4-8`
386
395
  - OpenAI: `gpt-4o`
387
396
  - Gemini: `gemini-1.5-pro`
388
397
 
@@ -3,5 +3,5 @@ from .search import ask
3
3
  from .planner import plan
4
4
  from .redteam import redteam
5
5
 
6
- __version__ = "0.3.7"
6
+ __version__ = "0.3.8"
7
7
  __all__ = ["evaluate", "ask", "plan", "redteam"]
@@ -9,7 +9,7 @@ from .search import ask
9
9
 
10
10
  def _add_provider_args(parser: argparse.ArgumentParser) -> None:
11
11
  parser.add_argument("--provider", help='AI provider: "anthropic", "openai", or "gemini"')
12
- parser.add_argument("--model", help="Specific model to use (e.g. claude-opus-4-6, gpt-4o)")
12
+ parser.add_argument("--model", help="Specific model to use (e.g. claude-opus-4-8, gpt-4o)")
13
13
 
14
14
 
15
15
  def _cmd_plan(args: argparse.Namespace) -> None:
@@ -11,7 +11,7 @@ class Provider(Enum):
11
11
 
12
12
  # Default models for each provider
13
13
  DEFAULT_MODELS = {
14
- Provider.ANTHROPIC: "claude-opus-4-6",
14
+ Provider.ANTHROPIC: "claude-opus-4-8",
15
15
  Provider.OPENAI: "gpt-4o",
16
16
  Provider.GEMINI: "gemini-1.5-pro"
17
17
  }
@@ -29,7 +29,7 @@ ALTERNATIVES
29
29
  """
30
30
 
31
31
 
32
- def plan(question: str, data_description: str = None, goal: str = None, provider: str = None, model: str = None, system_prompt: str = None) -> str:
32
+ def plan(question: str, data_description: str = None, goal: str = None, provider: str = None, model: str = None, system_prompt: str = None, max_tokens: int = 1024) -> str:
33
33
  """
34
34
  Recommend the right analytical approach for your problem.
35
35
 
@@ -42,6 +42,7 @@ def plan(question: str, data_description: str = None, goal: str = None, provider
42
42
  If not specified, defaults to "anthropic" or infers from model.
43
43
  model: Optional. The specific model to use. If not specified, uses the provider's default.
44
44
  system_prompt: Optional. A custom system prompt to override the default planner persona.
45
+ max_tokens: Optional. Maximum tokens in the response. Defaults to 1024.
45
46
 
46
47
  Returns:
47
48
  A structured analytical plan covering the recommended approach, assumptions,
@@ -66,5 +67,5 @@ def plan(question: str, data_description: str = None, goal: str = None, provider
66
67
  system_prompt=system_prompt or SYSTEM_PROMPT,
67
68
  user_message=user_message,
68
69
  model=model,
69
- max_tokens=1024
70
+ max_tokens=max_tokens
70
71
  )
@@ -39,7 +39,7 @@ HARDEST QUESTION TO ANSWER
39
39
  """
40
40
 
41
41
 
42
- def redteam(text: str, stakeholder: str = None, provider: str = None, model: str = None, system_prompt: str = None) -> str:
42
+ def redteam(text: str, stakeholder: str = None, provider: str = None, model: str = None, system_prompt: str = None, max_tokens: int = 1024) -> str:
43
43
  """
44
44
  Red-team a data science analysis writeup from the perspective of a skeptical stakeholder.
45
45
 
@@ -53,6 +53,7 @@ def redteam(text: str, stakeholder: str = None, provider: str = None, model: str
53
53
  model: Optional. The specific model to use. If not specified, uses the provider's default.
54
54
  system_prompt: Optional. A custom system prompt to fully override the default red team persona.
55
55
  When provided, the stakeholder parameter is ignored.
56
+ max_tokens: Optional. Maximum tokens in the response. Defaults to 1024.
56
57
 
57
58
  Returns:
58
59
  The 3-5 hardest critiques the stakeholder would make, plus the single
@@ -81,5 +82,5 @@ def redteam(text: str, stakeholder: str = None, provider: str = None, model: str
81
82
  system_prompt=system_prompt,
82
83
  user_message=user_message,
83
84
  model=model,
84
- max_tokens=1024
85
+ max_tokens=max_tokens
85
86
  )
@@ -42,7 +42,7 @@ BOTTOM LINE
42
42
  [one sentence]
43
43
  """
44
44
 
45
- def evaluate(text: str, provider: str = None, model: str = None, system_prompt: str = None) -> str:
45
+ def evaluate(text: str, provider: str = None, model: str = None, system_prompt: str = None, max_tokens: int = 1024) -> str:
46
46
  """
47
47
  Evaluate a data science analysis writeup and return structured feedback.
48
48
 
@@ -52,6 +52,7 @@ def evaluate(text: str, provider: str = None, model: str = None, system_prompt:
52
52
  If not specified, defaults to "anthropic" or infers from model.
53
53
  model: Optional. The specific model to use. If not specified, uses the provider's default.
54
54
  system_prompt: Optional. A custom system prompt to override the default reviewer persona.
55
+ max_tokens: Optional. Maximum tokens in the response. Defaults to 1024.
55
56
 
56
57
  Returns:
57
58
  Structured feedback across four dimensions.
@@ -71,5 +72,5 @@ def evaluate(text: str, provider: str = None, model: str = None, system_prompt:
71
72
  system_prompt=system_prompt or SYSTEM_PROMPT,
72
73
  user_message=user_message,
73
74
  model=model,
74
- max_tokens=1024
75
+ max_tokens=max_tokens
75
76
  )
@@ -56,7 +56,7 @@ DEFAULT_SYSTEM_PROMPT = (
56
56
  )
57
57
 
58
58
 
59
- def ask(question: str, source: str = None, text: str = None, provider: str = None, model: str = None, system_prompt: str = None) -> str:
59
+ def ask(question: str, source: str = None, text: str = None, provider: str = None, model: str = None, system_prompt: str = None, max_tokens: int = 1024) -> str:
60
60
  """
61
61
  Ask a question across a collection of analysis documents or raw text.
62
62
 
@@ -68,6 +68,7 @@ def ask(question: str, source: str = None, text: str = None, provider: str = Non
68
68
  If not specified, defaults to "anthropic" or infers from model.
69
69
  model: Optional. The specific model to use. If not specified, uses the provider's default.
70
70
  system_prompt: Optional. A custom system prompt to override the default answering persona.
71
+ max_tokens: Optional. Maximum tokens in the response. Defaults to 1024.
71
72
 
72
73
  Returns:
73
74
  An answer grounded in the provided documents.
@@ -121,5 +122,5 @@ def ask(question: str, source: str = None, text: str = None, provider: str = Non
121
122
  system_prompt=system_prompt or DEFAULT_SYSTEM_PROMPT,
122
123
  user_message=user_message,
123
124
  model=model,
124
- max_tokens=1024
125
+ max_tokens=max_tokens
125
126
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bridgekit
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: AI tools that make you a better data scientist, not a redundant one.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://usebridgekit.com
@@ -136,6 +136,9 @@ onboarding users to reporting as a growth lever.
136
136
  """
137
137
 
138
138
  print(evaluate(text))
139
+
140
+ # Override for longer analyses
141
+ print(evaluate(text, max_tokens=2048))
139
142
  ```
140
143
 
141
144
  **Output:**
@@ -190,6 +193,9 @@ Supports `.txt`, `.md`, `.pdf`, `.docx`, `.pptx`, and `.ipynb` files.
190
193
  from bridgekit import ask
191
194
 
192
195
  print(ask("what drove churn in Q3?", source="reports/"))
196
+
197
+ # Override for longer responses
198
+ print(ask("what drove churn in Q3?", source="reports/", max_tokens=2048))
193
199
  ```
194
200
 
195
201
  **From raw text:**
@@ -234,7 +240,7 @@ print(plan(
234
240
  ))
235
241
  ```
236
242
 
237
- `data_description` and `goal` are optional — the more context you provide, the more tailored the recommendation.
243
+ `data_description`, `goal`, and `max_tokens` are optional — the more context you provide, the more tailored the recommendation.
238
244
 
239
245
  **`goal` examples:** `"causal inference"`, `"prediction"`, `"segmentation"`, `"hypothesis testing"`, `"exploration"`
240
246
 
@@ -297,6 +303,9 @@ print(redteam(text))
297
303
  # Or specify a stakeholder
298
304
  print(redteam(text, stakeholder="VP of Engineering"))
299
305
  print(redteam(text, stakeholder="VP of Marketing"))
306
+
307
+ # Override for longer responses
308
+ print(redteam(text, max_tokens=2048))
300
309
  ```
301
310
 
302
311
  Same writeup, different attack angles:
@@ -414,7 +423,7 @@ Bridgekit automatically detects the provider from model names:
414
423
  - Models starting with "gemini" → Google Gemini
415
424
 
416
425
  **Default models by provider:**
417
- - Anthropic: `claude-3-5-sonnet-20241022`
426
+ - Anthropic: `claude-opus-4-8`
418
427
  - OpenAI: `gpt-4o`
419
428
  - Gemini: `gemini-1.5-pro`
420
429
 
@@ -15,6 +15,7 @@ bridgekit.egg-info/dependency_links.txt
15
15
  bridgekit.egg-info/entry_points.txt
16
16
  bridgekit.egg-info/requires.txt
17
17
  bridgekit.egg-info/top_level.txt
18
+ tests/test_cli.py
18
19
  tests/test_config.py
19
20
  tests/test_planner.py
20
21
  tests/test_providers.py
@@ -7,7 +7,7 @@ include = ["bridgekit*"]
7
7
 
8
8
  [project]
9
9
  name = "bridgekit"
10
- version = "0.3.7"
10
+ version = "0.3.8"
11
11
  description = "AI tools that make you a better data scientist, not a redundant one."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.9"
@@ -0,0 +1,175 @@
1
+ import sys
2
+ import pytest
3
+ from unittest.mock import patch
4
+
5
+
6
+ FAKE_PLAN = "BRIDGEKIT ANALYSIS PLAN\n─────\nRECOMMENDED APPROACH\nUse a t-test."
7
+ FAKE_REVIEW = "BRIDGEKIT ANALYSIS REVIEW\n─────\n1. CLARITY\n✅ STRONG Clear writing."
8
+ FAKE_REDTEAM = "BRIDGEKIT RED TEAM\n─────\nCRITIQUE 1: Sample Size\nHARDEST QUESTION TO ANSWER\nWhat is n?"
9
+ FAKE_SEARCH = "Based on the documents, the answer is 42."
10
+
11
+
12
+ class TestPlanCommand:
13
+ def test_basic_question(self, capsys):
14
+ with patch("bridgekit.cli.plan", return_value=FAKE_PLAN) as mock_plan:
15
+ with patch("sys.argv", ["bridgekit", "plan", "should I use a t-test?"]):
16
+ from bridgekit.cli import main
17
+ main()
18
+ mock_plan.assert_called_once_with(
19
+ question="should I use a t-test?",
20
+ data_description=None,
21
+ goal=None,
22
+ provider=None,
23
+ model=None,
24
+ )
25
+ assert FAKE_PLAN in capsys.readouterr().out
26
+
27
+ def test_with_data_and_goal(self, capsys):
28
+ with patch("bridgekit.cli.plan", return_value=FAKE_PLAN) as mock_plan:
29
+ with patch("sys.argv", ["bridgekit", "plan", "my question",
30
+ "--data", "50 rows", "--goal", "compare means"]):
31
+ from bridgekit.cli import main
32
+ main()
33
+ mock_plan.assert_called_once_with(
34
+ question="my question",
35
+ data_description="50 rows",
36
+ goal="compare means",
37
+ provider=None,
38
+ model=None,
39
+ )
40
+
41
+ def test_with_provider_and_model(self):
42
+ with patch("bridgekit.cli.plan", return_value=FAKE_PLAN) as mock_plan:
43
+ with patch("sys.argv", ["bridgekit", "plan", "my question",
44
+ "--provider", "openai", "--model", "gpt-4o"]):
45
+ from bridgekit.cli import main
46
+ main()
47
+ mock_plan.assert_called_once_with(
48
+ question="my question",
49
+ data_description=None,
50
+ goal=None,
51
+ provider="openai",
52
+ model="gpt-4o",
53
+ )
54
+
55
+ def test_missing_question_exits(self):
56
+ with patch("sys.argv", ["bridgekit", "plan"]):
57
+ from bridgekit.cli import main
58
+ with pytest.raises(SystemExit):
59
+ main()
60
+
61
+ def test_environment_error_exits(self, capsys):
62
+ with patch("bridgekit.cli.plan", side_effect=EnvironmentError("ANTHROPIC_API_KEY not found")):
63
+ with patch("sys.argv", ["bridgekit", "plan", "my question"]):
64
+ from bridgekit.cli import main
65
+ with pytest.raises(SystemExit) as exc:
66
+ main()
67
+ assert exc.value.code == 1
68
+ assert "ANTHROPIC_API_KEY" in capsys.readouterr().err
69
+
70
+
71
+ class TestReviewCommand:
72
+ def test_basic_text(self, capsys):
73
+ with patch("bridgekit.cli.evaluate", return_value=FAKE_REVIEW) as mock_evaluate:
74
+ with patch("sys.argv", ["bridgekit", "review", "my analysis text"]):
75
+ from bridgekit.cli import main
76
+ main()
77
+ mock_evaluate.assert_called_once_with(
78
+ text="my analysis text",
79
+ provider=None,
80
+ model=None,
81
+ )
82
+ assert FAKE_REVIEW in capsys.readouterr().out
83
+
84
+ def test_missing_text_exits(self):
85
+ with patch("sys.argv", ["bridgekit", "review"]):
86
+ from bridgekit.cli import main
87
+ with pytest.raises(SystemExit):
88
+ main()
89
+
90
+
91
+ class TestRedteamCommand:
92
+ def test_basic_text(self, capsys):
93
+ with patch("bridgekit.cli.redteam", return_value=FAKE_REDTEAM) as mock_redteam:
94
+ with patch("sys.argv", ["bridgekit", "redteam", "my analysis text"]):
95
+ from bridgekit.cli import main
96
+ main()
97
+ mock_redteam.assert_called_once_with(
98
+ text="my analysis text",
99
+ stakeholder=None,
100
+ provider=None,
101
+ model=None,
102
+ )
103
+ assert FAKE_REDTEAM in capsys.readouterr().out
104
+
105
+ def test_with_stakeholder(self):
106
+ with patch("bridgekit.cli.redteam", return_value=FAKE_REDTEAM) as mock_redteam:
107
+ with patch("sys.argv", ["bridgekit", "redteam", "my analysis text",
108
+ "--stakeholder", "VP of Finance"]):
109
+ from bridgekit.cli import main
110
+ main()
111
+ mock_redteam.assert_called_once_with(
112
+ text="my analysis text",
113
+ stakeholder="VP of Finance",
114
+ provider=None,
115
+ model=None,
116
+ )
117
+
118
+ def test_missing_text_exits(self):
119
+ with patch("sys.argv", ["bridgekit", "redteam"]):
120
+ from bridgekit.cli import main
121
+ with pytest.raises(SystemExit):
122
+ main()
123
+
124
+
125
+ class TestSearchCommand:
126
+ def test_with_source(self, capsys):
127
+ with patch("bridgekit.cli.ask", return_value=FAKE_SEARCH) as mock_ask:
128
+ with patch("sys.argv", ["bridgekit", "search", "my question",
129
+ "--source", "./my_docs"]):
130
+ from bridgekit.cli import main
131
+ main()
132
+ mock_ask.assert_called_once_with(
133
+ question="my question",
134
+ source="./my_docs",
135
+ text=None,
136
+ provider=None,
137
+ model=None,
138
+ )
139
+ assert FAKE_SEARCH in capsys.readouterr().out
140
+
141
+ def test_with_text(self):
142
+ with patch("bridgekit.cli.ask", return_value=FAKE_SEARCH) as mock_ask:
143
+ with patch("sys.argv", ["bridgekit", "search", "my question",
144
+ "--text", "some raw text"]):
145
+ from bridgekit.cli import main
146
+ main()
147
+ mock_ask.assert_called_once_with(
148
+ question="my question",
149
+ source=None,
150
+ text="some raw text",
151
+ provider=None,
152
+ model=None,
153
+ )
154
+
155
+ def test_missing_source_and_text_exits(self, capsys):
156
+ with patch("sys.argv", ["bridgekit", "search", "my question"]):
157
+ from bridgekit.cli import main
158
+ with pytest.raises(SystemExit) as exc:
159
+ main()
160
+ assert exc.value.code == 1
161
+ assert "error" in capsys.readouterr().err
162
+
163
+ def test_missing_question_exits(self):
164
+ with patch("sys.argv", ["bridgekit", "search"]):
165
+ from bridgekit.cli import main
166
+ with pytest.raises(SystemExit):
167
+ main()
168
+
169
+
170
+ class TestNoCommand:
171
+ def test_no_subcommand_exits(self):
172
+ with patch("sys.argv", ["bridgekit"]):
173
+ from bridgekit.cli import main
174
+ with pytest.raises(SystemExit):
175
+ main()
@@ -192,3 +192,33 @@ class TestPlanOptionalParameters:
192
192
  content = str(messages_arg)
193
193
  assert "5,000 users split 50/50." in content
194
194
  assert "causal inference" in content
195
+
196
+
197
+ class TestPlanMaxTokens:
198
+ """plan() should pass max_tokens through to the API."""
199
+
200
+ def test_default_max_tokens_is_1024(self):
201
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
202
+ with patch("anthropic.Anthropic") as MockAnthropic:
203
+ mock_client = MagicMock()
204
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
205
+ MockAnthropic.return_value = mock_client
206
+
207
+ from bridgekit.planner import plan
208
+ plan("Does our new onboarding flow increase upgrade rates?")
209
+
210
+ call_kwargs = mock_client.messages.create.call_args
211
+ assert call_kwargs.kwargs.get("max_tokens") == 1024
212
+
213
+ def test_custom_max_tokens_reaches_api(self):
214
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
215
+ with patch("anthropic.Anthropic") as MockAnthropic:
216
+ mock_client = MagicMock()
217
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
218
+ MockAnthropic.return_value = mock_client
219
+
220
+ from bridgekit.planner import plan
221
+ plan("Does our new onboarding flow increase upgrade rates?", max_tokens=2048)
222
+
223
+ call_kwargs = mock_client.messages.create.call_args
224
+ assert call_kwargs.kwargs.get("max_tokens") == 2048
@@ -153,3 +153,33 @@ class TestRedteamCustomSystemPrompt:
153
153
 
154
154
  call_kwargs = mock_client.messages.create.call_args
155
155
  assert call_kwargs.kwargs.get("system") == custom_prompt
156
+
157
+
158
+ class TestRedteamMaxTokens:
159
+ """redteam() should pass max_tokens through to the API."""
160
+
161
+ def test_default_max_tokens_is_1024(self):
162
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
163
+ with patch("anthropic.Anthropic") as MockAnthropic:
164
+ mock_client = MagicMock()
165
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
166
+ MockAnthropic.return_value = mock_client
167
+
168
+ from bridgekit.redteam import redteam
169
+ redteam("Some analysis text.")
170
+
171
+ call_kwargs = mock_client.messages.create.call_args
172
+ assert call_kwargs.kwargs.get("max_tokens") == 1024
173
+
174
+ def test_custom_max_tokens_reaches_api(self):
175
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
176
+ with patch("anthropic.Anthropic") as MockAnthropic:
177
+ mock_client = MagicMock()
178
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
179
+ MockAnthropic.return_value = mock_client
180
+
181
+ from bridgekit.redteam import redteam
182
+ redteam("Some analysis text.", max_tokens=2048)
183
+
184
+ call_kwargs = mock_client.messages.create.call_args
185
+ assert call_kwargs.kwargs.get("max_tokens") == 2048
@@ -176,3 +176,33 @@ class TestEvaluateCustomSystemPrompt:
176
176
 
177
177
  call_kwargs = mock_client.messages.create.call_args
178
178
  assert call_kwargs.kwargs.get("system") == custom_prompt
179
+
180
+
181
+ class TestEvaluateMaxTokens:
182
+ """evaluate() should pass max_tokens through to the API."""
183
+
184
+ def test_default_max_tokens_is_1024(self):
185
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
186
+ with patch("anthropic.Anthropic") as MockAnthropic:
187
+ mock_client = MagicMock()
188
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
189
+ MockAnthropic.return_value = mock_client
190
+
191
+ from bridgekit.reviewer import evaluate
192
+ evaluate("Some analysis text.")
193
+
194
+ call_kwargs = mock_client.messages.create.call_args
195
+ assert call_kwargs.kwargs.get("max_tokens") == 1024
196
+
197
+ def test_custom_max_tokens_reaches_api(self):
198
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
199
+ with patch("anthropic.Anthropic") as MockAnthropic:
200
+ mock_client = MagicMock()
201
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_RESPONSE)
202
+ MockAnthropic.return_value = mock_client
203
+
204
+ from bridgekit.reviewer import evaluate
205
+ evaluate("Some analysis text.", max_tokens=2048)
206
+
207
+ call_kwargs = mock_client.messages.create.call_args
208
+ assert call_kwargs.kwargs.get("max_tokens") == 2048
@@ -257,3 +257,45 @@ class TestAskWithSourceFolder:
257
257
  from bridgekit.search import ask
258
258
  with pytest.raises(ValueError, match="No content found"):
259
259
  ask("What happened?", source=tmpdir)
260
+
261
+
262
+ class TestAskMaxTokens:
263
+ """ask() should pass max_tokens through to the API."""
264
+
265
+ def test_default_max_tokens_is_1024(self):
266
+ mock_chromadb, mock_ef = _make_mock_chromadb()
267
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
268
+ with patch("anthropic.Anthropic") as MockAnthropic, \
269
+ patch("chromadb.Client", mock_chromadb.Client), \
270
+ patch(
271
+ "chromadb.utils.embedding_functions.SentenceTransformerEmbeddingFunction",
272
+ mock_ef,
273
+ ):
274
+ mock_client = MagicMock()
275
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_ANSWER)
276
+ MockAnthropic.return_value = mock_client
277
+
278
+ from bridgekit.search import ask
279
+ ask("What was the conversion rate?", text="The conversion rate increased by 12%.")
280
+
281
+ call_kwargs = mock_client.messages.create.call_args
282
+ assert call_kwargs.kwargs.get("max_tokens") == 1024
283
+
284
+ def test_custom_max_tokens_reaches_api(self):
285
+ mock_chromadb, mock_ef = _make_mock_chromadb()
286
+ with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
287
+ with patch("anthropic.Anthropic") as MockAnthropic, \
288
+ patch("chromadb.Client", mock_chromadb.Client), \
289
+ patch(
290
+ "chromadb.utils.embedding_functions.SentenceTransformerEmbeddingFunction",
291
+ mock_ef,
292
+ ):
293
+ mock_client = MagicMock()
294
+ mock_client.messages.create.return_value = _make_mock_message(FAKE_ANSWER)
295
+ MockAnthropic.return_value = mock_client
296
+
297
+ from bridgekit.search import ask
298
+ ask("What was the conversion rate?", text="The conversion rate increased by 12%.", max_tokens=2048)
299
+
300
+ call_kwargs = mock_client.messages.create.call_args
301
+ assert call_kwargs.kwargs.get("max_tokens") == 2048
File without changes
File without changes