codeframe-ai 0.9.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.
- codeframe/__init__.py +11 -0
- codeframe/__main__.py +20 -0
- codeframe/adapters/__init__.py +5 -0
- codeframe/adapters/e2b/__init__.py +13 -0
- codeframe/adapters/e2b/adapter.py +342 -0
- codeframe/adapters/e2b/budget.py +71 -0
- codeframe/adapters/e2b/credential_scanner.py +134 -0
- codeframe/adapters/llm/__init__.py +92 -0
- codeframe/adapters/llm/anthropic.py +414 -0
- codeframe/adapters/llm/base.py +444 -0
- codeframe/adapters/llm/mock.py +281 -0
- codeframe/adapters/llm/openai.py +483 -0
- codeframe/agents/__init__.py +8 -0
- codeframe/agents/dependency_resolver.py +714 -0
- codeframe/auth/__init__.py +16 -0
- codeframe/auth/api_key_router.py +238 -0
- codeframe/auth/api_keys.py +156 -0
- codeframe/auth/dependencies.py +358 -0
- codeframe/auth/manager.py +178 -0
- codeframe/auth/models.py +30 -0
- codeframe/auth/router.py +93 -0
- codeframe/auth/schemas.py +15 -0
- codeframe/auth/scopes.py +53 -0
- codeframe/cli/__init__.py +12 -0
- codeframe/cli/__main__.py +20 -0
- codeframe/cli/api_client.py +275 -0
- codeframe/cli/app.py +5688 -0
- codeframe/cli/auth.py +122 -0
- codeframe/cli/auth_commands.py +958 -0
- codeframe/cli/commands/__init__.py +5 -0
- codeframe/cli/config_commands.py +79 -0
- codeframe/cli/dashboard_commands.py +67 -0
- codeframe/cli/engines_commands.py +205 -0
- codeframe/cli/env_commands.py +409 -0
- codeframe/cli/helpers.py +56 -0
- codeframe/cli/hooks_commands.py +208 -0
- codeframe/cli/import_commands.py +129 -0
- codeframe/cli/pr_commands.py +549 -0
- codeframe/cli/proof_commands.py +415 -0
- codeframe/cli/stats_commands.py +311 -0
- codeframe/cli/telemetry_runtime.py +153 -0
- codeframe/cli/validators.py +123 -0
- codeframe/config/rate_limits.py +165 -0
- codeframe/core/__init__.py +15 -0
- codeframe/core/adapters/__init__.py +43 -0
- codeframe/core/adapters/agent_adapter.py +114 -0
- codeframe/core/adapters/builtin.py +326 -0
- codeframe/core/adapters/claude_code.py +62 -0
- codeframe/core/adapters/codex.py +393 -0
- codeframe/core/adapters/git_utils.py +40 -0
- codeframe/core/adapters/kilocode.py +126 -0
- codeframe/core/adapters/opencode.py +48 -0
- codeframe/core/adapters/streaming_chat.py +483 -0
- codeframe/core/adapters/subprocess_adapter.py +213 -0
- codeframe/core/adapters/verification_wrapper.py +269 -0
- codeframe/core/agent.py +2183 -0
- codeframe/core/agents_config.py +569 -0
- codeframe/core/api_key_service.py +211 -0
- codeframe/core/artifacts.py +428 -0
- codeframe/core/blocker_detection.py +218 -0
- codeframe/core/blockers.py +433 -0
- codeframe/core/checkpoints.py +481 -0
- codeframe/core/conductor.py +2255 -0
- codeframe/core/config.py +827 -0
- codeframe/core/config_watcher.py +268 -0
- codeframe/core/context.py +542 -0
- codeframe/core/context_packager.py +234 -0
- codeframe/core/credentials.py +735 -0
- codeframe/core/dependency_analyzer.py +229 -0
- codeframe/core/dependency_graph.py +290 -0
- codeframe/core/diagnostic_agent.py +712 -0
- codeframe/core/diagnostics.py +616 -0
- codeframe/core/editor.py +556 -0
- codeframe/core/engine_registry.py +256 -0
- codeframe/core/engine_stats.py +231 -0
- codeframe/core/environment.py +697 -0
- codeframe/core/events.py +375 -0
- codeframe/core/executor.py +1005 -0
- codeframe/core/fix_tracker.py +480 -0
- codeframe/core/gates.py +1322 -0
- codeframe/core/git.py +477 -0
- codeframe/core/github_connect_service.py +178 -0
- codeframe/core/github_integration_config.py +118 -0
- codeframe/core/github_issues_service.py +449 -0
- codeframe/core/hooks.py +184 -0
- codeframe/core/importers/__init__.py +1 -0
- codeframe/core/importers/ralph.py +540 -0
- codeframe/core/installer.py +650 -0
- codeframe/core/models.py +1026 -0
- codeframe/core/notifications_config.py +183 -0
- codeframe/core/planner.py +437 -0
- codeframe/core/prd.py +670 -0
- codeframe/core/prd_discovery.py +1118 -0
- codeframe/core/prd_stress_test.py +499 -0
- codeframe/core/progress.py +126 -0
- codeframe/core/proof/__init__.py +34 -0
- codeframe/core/proof/capture.py +79 -0
- codeframe/core/proof/evidence.py +56 -0
- codeframe/core/proof/ledger.py +574 -0
- codeframe/core/proof/models.py +162 -0
- codeframe/core/proof/obligations.py +103 -0
- codeframe/core/proof/runner.py +233 -0
- codeframe/core/proof/scope.py +81 -0
- codeframe/core/proof/stubs.py +156 -0
- codeframe/core/quick_fixes.py +558 -0
- codeframe/core/react_agent.py +1650 -0
- codeframe/core/reconciliation.py +183 -0
- codeframe/core/replay.py +788 -0
- codeframe/core/review.py +285 -0
- codeframe/core/runtime.py +1134 -0
- codeframe/core/sandbox/__init__.py +27 -0
- codeframe/core/sandbox/context.py +98 -0
- codeframe/core/sandbox/worktree.py +20 -0
- codeframe/core/schedule.py +396 -0
- codeframe/core/stall_detector.py +71 -0
- codeframe/core/stall_monitor.py +134 -0
- codeframe/core/state_machine.py +121 -0
- codeframe/core/streaming.py +502 -0
- codeframe/core/task_tree.py +400 -0
- codeframe/core/tasks.py +1022 -0
- codeframe/core/telemetry.py +232 -0
- codeframe/core/templates.py +221 -0
- codeframe/core/tools.py +942 -0
- codeframe/core/workspace.py +887 -0
- codeframe/core/worktrees.py +276 -0
- codeframe/git/__init__.py +5 -0
- codeframe/git/github_integration.py +505 -0
- codeframe/lib/__init__.py +0 -0
- codeframe/lib/audit_logger.py +248 -0
- codeframe/lib/metrics_tracker.py +800 -0
- codeframe/lib/quality/__init__.py +7 -0
- codeframe/lib/quality/complexity_analyzer.py +316 -0
- codeframe/lib/quality/owasp_patterns.py +284 -0
- codeframe/lib/quality/security_scanner.py +250 -0
- codeframe/lib/rate_limiter.py +312 -0
- codeframe/notifications/__init__.py +0 -0
- codeframe/notifications/webhook.py +380 -0
- codeframe/planning/__init__.py +30 -0
- codeframe/planning/issue_generator.py +219 -0
- codeframe/planning/prd_template_functions.py +137 -0
- codeframe/planning/prd_templates.py +975 -0
- codeframe/planning/task_scheduler.py +511 -0
- codeframe/planning/task_templates.py +533 -0
- codeframe/platform_store/__init__.py +5 -0
- codeframe/platform_store/database.py +277 -0
- codeframe/platform_store/repositories/__init__.py +24 -0
- codeframe/platform_store/repositories/api_key_repository.py +245 -0
- codeframe/platform_store/repositories/audit_repository.py +67 -0
- codeframe/platform_store/repositories/base.py +295 -0
- codeframe/platform_store/repositories/interactive_sessions.py +165 -0
- codeframe/platform_store/repositories/token_repository.py +598 -0
- codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
- codeframe/platform_store/schema_manager.py +321 -0
- codeframe/templates/AGENTS.md.default +94 -0
- codeframe/tui/__init__.py +5 -0
- codeframe/tui/app.py +256 -0
- codeframe/tui/data_service.py +103 -0
- codeframe/ui/__init__.py +0 -0
- codeframe/ui/dependencies.py +103 -0
- codeframe/ui/models.py +999 -0
- codeframe/ui/response_models.py +201 -0
- codeframe/ui/routers/__init__.py +5 -0
- codeframe/ui/routers/_helpers.py +29 -0
- codeframe/ui/routers/batches_v2.py +315 -0
- codeframe/ui/routers/blockers_v2.py +320 -0
- codeframe/ui/routers/checkpoints_v2.py +310 -0
- codeframe/ui/routers/costs_v2.py +322 -0
- codeframe/ui/routers/diagnose_v2.py +225 -0
- codeframe/ui/routers/discovery_v2.py +417 -0
- codeframe/ui/routers/environment_v2.py +284 -0
- codeframe/ui/routers/events_v2.py +75 -0
- codeframe/ui/routers/gates_v2.py +166 -0
- codeframe/ui/routers/git_v2.py +284 -0
- codeframe/ui/routers/github_integrations_v2.py +532 -0
- codeframe/ui/routers/interactive_sessions_v2.py +238 -0
- codeframe/ui/routers/pr_v2.py +709 -0
- codeframe/ui/routers/prd_v2.py +695 -0
- codeframe/ui/routers/proof_v2.py +755 -0
- codeframe/ui/routers/review_v2.py +360 -0
- codeframe/ui/routers/schedule_v2.py +214 -0
- codeframe/ui/routers/session_chat_ws.py +354 -0
- codeframe/ui/routers/settings_v2.py +562 -0
- codeframe/ui/routers/streaming_v2.py +155 -0
- codeframe/ui/routers/tasks_v2.py +1098 -0
- codeframe/ui/routers/templates_v2.py +232 -0
- codeframe/ui/routers/terminal_ws.py +267 -0
- codeframe/ui/routers/workspace_v2.py +527 -0
- codeframe/ui/server.py +568 -0
- codeframe/ui/shared.py +241 -0
- codeframe/workspace/__init__.py +5 -0
- codeframe/workspace/manager.py +249 -0
- codeframe_ai-0.9.0.dist-info/METADATA +517 -0
- codeframe_ai-0.9.0.dist-info/RECORD +197 -0
- codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
- codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
- codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
- codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Mock LLM provider for testing.
|
|
2
|
+
|
|
3
|
+
Provides predictable responses without making API calls.
|
|
4
|
+
Supports configurable responses and call tracking.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import AsyncIterator, Callable, Iterator, Optional
|
|
9
|
+
|
|
10
|
+
from codeframe.adapters.llm.base import (
|
|
11
|
+
LLMProvider,
|
|
12
|
+
LLMResponse,
|
|
13
|
+
ModelSelector,
|
|
14
|
+
Purpose,
|
|
15
|
+
StreamChunk,
|
|
16
|
+
Tool,
|
|
17
|
+
ToolCall,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MockProvider(LLMProvider):
|
|
22
|
+
"""Mock LLM provider for testing.
|
|
23
|
+
|
|
24
|
+
Tracks all calls and returns configurable responses.
|
|
25
|
+
Useful for unit tests and development without API costs.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
default_response: str = "Mock response",
|
|
31
|
+
model_selector: Optional[ModelSelector] = None,
|
|
32
|
+
):
|
|
33
|
+
"""Initialize the mock provider.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
default_response: Default response text
|
|
37
|
+
model_selector: Custom model selector
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(model_selector)
|
|
40
|
+
self.default_response = default_response
|
|
41
|
+
self.calls: list[dict] = []
|
|
42
|
+
self.responses: list[LLMResponse] = []
|
|
43
|
+
self.response_index = 0
|
|
44
|
+
self.response_handler: Optional[Callable[[list[dict]], LLMResponse]] = None
|
|
45
|
+
self.stream_chunks: list[list[StreamChunk]] = []
|
|
46
|
+
self.stream_index = 0
|
|
47
|
+
|
|
48
|
+
def add_response(self, response: LLMResponse) -> None:
|
|
49
|
+
"""Add a canned response to the queue.
|
|
50
|
+
|
|
51
|
+
Responses are returned in order for subsequent calls.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
response: Response to add to queue
|
|
55
|
+
"""
|
|
56
|
+
self.responses.append(response)
|
|
57
|
+
|
|
58
|
+
def add_text_response(self, content: str) -> None:
|
|
59
|
+
"""Add a simple text response to the queue.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
content: Text content for the response
|
|
63
|
+
"""
|
|
64
|
+
self.responses.append(LLMResponse(content=content))
|
|
65
|
+
|
|
66
|
+
def add_tool_response(self, tool_calls: list[ToolCall], content: str = "") -> None:
|
|
67
|
+
"""Add a response with tool calls.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
tool_calls: Tool calls to include
|
|
71
|
+
content: Optional text content
|
|
72
|
+
"""
|
|
73
|
+
self.responses.append(
|
|
74
|
+
LLMResponse(
|
|
75
|
+
content=content,
|
|
76
|
+
tool_calls=tool_calls,
|
|
77
|
+
stop_reason="tool_use",
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def set_response_handler(
|
|
82
|
+
self, handler: Callable[[list[dict]], LLMResponse]
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Set a custom handler for generating responses.
|
|
85
|
+
|
|
86
|
+
The handler receives the messages and returns a response.
|
|
87
|
+
Takes precedence over queued responses.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
handler: Function that takes messages and returns LLMResponse
|
|
91
|
+
"""
|
|
92
|
+
self.response_handler = handler
|
|
93
|
+
|
|
94
|
+
def complete(
|
|
95
|
+
self,
|
|
96
|
+
messages: list[dict],
|
|
97
|
+
purpose: Purpose = Purpose.EXECUTION,
|
|
98
|
+
tools: Optional[list[Tool]] = None,
|
|
99
|
+
max_tokens: int = 4096,
|
|
100
|
+
temperature: float = 0.0,
|
|
101
|
+
system: Optional[str] = None,
|
|
102
|
+
) -> LLMResponse:
|
|
103
|
+
"""Return a mock completion.
|
|
104
|
+
|
|
105
|
+
Tracks the call and returns from queue or default.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
messages: Conversation messages
|
|
109
|
+
purpose: Purpose of call (for model selection)
|
|
110
|
+
tools: Available tools (tracked but not used)
|
|
111
|
+
max_tokens: Maximum tokens (tracked but not used)
|
|
112
|
+
temperature: Temperature (tracked but not used)
|
|
113
|
+
system: System prompt (tracked but not used)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Queued response, handler result, or default response
|
|
117
|
+
"""
|
|
118
|
+
model = self.get_model(purpose)
|
|
119
|
+
|
|
120
|
+
# Track the call
|
|
121
|
+
self.calls.append(
|
|
122
|
+
{
|
|
123
|
+
"messages": messages,
|
|
124
|
+
"purpose": purpose,
|
|
125
|
+
"tools": tools,
|
|
126
|
+
"max_tokens": max_tokens,
|
|
127
|
+
"temperature": temperature,
|
|
128
|
+
"system": system,
|
|
129
|
+
"model": model,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Custom handler takes precedence
|
|
134
|
+
if self.response_handler:
|
|
135
|
+
return self.response_handler(messages)
|
|
136
|
+
|
|
137
|
+
# Return queued response if available
|
|
138
|
+
if self.response_index < len(self.responses):
|
|
139
|
+
response = self.responses[self.response_index]
|
|
140
|
+
self.response_index += 1
|
|
141
|
+
return response
|
|
142
|
+
|
|
143
|
+
# Default response
|
|
144
|
+
return LLMResponse(
|
|
145
|
+
content=self.default_response,
|
|
146
|
+
model=model,
|
|
147
|
+
input_tokens=len(str(messages)),
|
|
148
|
+
output_tokens=len(self.default_response),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def stream(
|
|
152
|
+
self,
|
|
153
|
+
messages: list[dict],
|
|
154
|
+
purpose: Purpose = Purpose.EXECUTION,
|
|
155
|
+
max_tokens: int = 4096,
|
|
156
|
+
temperature: float = 0.0,
|
|
157
|
+
system: Optional[str] = None,
|
|
158
|
+
) -> Iterator[str]:
|
|
159
|
+
"""Stream mock response word by word.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
messages: Conversation messages
|
|
163
|
+
purpose: Purpose of call
|
|
164
|
+
max_tokens: Maximum tokens
|
|
165
|
+
temperature: Temperature
|
|
166
|
+
system: System prompt
|
|
167
|
+
|
|
168
|
+
Yields:
|
|
169
|
+
Words from the response
|
|
170
|
+
"""
|
|
171
|
+
response = self.complete(
|
|
172
|
+
messages=messages,
|
|
173
|
+
purpose=purpose,
|
|
174
|
+
max_tokens=max_tokens,
|
|
175
|
+
temperature=temperature,
|
|
176
|
+
system=system,
|
|
177
|
+
)
|
|
178
|
+
# Simulate streaming by yielding words
|
|
179
|
+
for word in response.content.split():
|
|
180
|
+
yield word + " "
|
|
181
|
+
|
|
182
|
+
def add_stream_chunks(self, chunks: list[StreamChunk]) -> None:
|
|
183
|
+
"""Add a sequence of StreamChunks for the next async_stream() call.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
chunks: Ordered list of StreamChunk objects to yield.
|
|
187
|
+
"""
|
|
188
|
+
self.stream_chunks.append(chunks)
|
|
189
|
+
|
|
190
|
+
async def async_stream(
|
|
191
|
+
self,
|
|
192
|
+
messages: list[dict],
|
|
193
|
+
system: str,
|
|
194
|
+
tools: list[dict],
|
|
195
|
+
model: str,
|
|
196
|
+
max_tokens: int,
|
|
197
|
+
interrupt_event: Optional[asyncio.Event] = None,
|
|
198
|
+
extended_thinking: bool = False,
|
|
199
|
+
) -> AsyncIterator[StreamChunk]:
|
|
200
|
+
"""Yield pre-configured StreamChunk sequences for testing.
|
|
201
|
+
|
|
202
|
+
Tracks each call in :attr:`calls` (same metadata as :meth:`complete`).
|
|
203
|
+
When pre-configured ``stream_chunks`` are available, yields them in
|
|
204
|
+
order. Otherwise falls back to a minimal ``text_delta`` +
|
|
205
|
+
``message_stop`` pair derived from the normal response queue
|
|
206
|
+
(``responses`` / ``response_handler`` / ``default_response``).
|
|
207
|
+
"""
|
|
208
|
+
# Track the call so tests can assert on it
|
|
209
|
+
self.calls.append(
|
|
210
|
+
{
|
|
211
|
+
"messages": messages,
|
|
212
|
+
"system": system,
|
|
213
|
+
"tools": tools,
|
|
214
|
+
"model": model,
|
|
215
|
+
"max_tokens": max_tokens,
|
|
216
|
+
"extended_thinking": extended_thinking,
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if self.stream_index < len(self.stream_chunks):
|
|
221
|
+
chunks = self.stream_chunks[self.stream_index]
|
|
222
|
+
self.stream_index += 1
|
|
223
|
+
else:
|
|
224
|
+
# Derive response text from the normal queue / handler
|
|
225
|
+
if self.response_handler:
|
|
226
|
+
resp = self.response_handler(messages)
|
|
227
|
+
text = resp.content
|
|
228
|
+
elif self.response_index < len(self.responses):
|
|
229
|
+
resp = self.responses[self.response_index]
|
|
230
|
+
self.response_index += 1
|
|
231
|
+
text = resp.content
|
|
232
|
+
else:
|
|
233
|
+
text = self.default_response
|
|
234
|
+
|
|
235
|
+
chunks = [
|
|
236
|
+
StreamChunk(type="text_delta", text=text),
|
|
237
|
+
StreamChunk(
|
|
238
|
+
type="message_stop",
|
|
239
|
+
stop_reason="end_turn",
|
|
240
|
+
input_tokens=len(str(messages)),
|
|
241
|
+
output_tokens=len(text),
|
|
242
|
+
tool_inputs_by_id={},
|
|
243
|
+
),
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
for chunk in chunks:
|
|
247
|
+
if interrupt_event and interrupt_event.is_set():
|
|
248
|
+
return
|
|
249
|
+
yield chunk
|
|
250
|
+
|
|
251
|
+
def reset(self) -> None:
|
|
252
|
+
"""Reset call tracking and response queue."""
|
|
253
|
+
self.calls.clear()
|
|
254
|
+
self.responses.clear()
|
|
255
|
+
self.response_index = 0
|
|
256
|
+
self.response_handler = None
|
|
257
|
+
self.stream_chunks.clear()
|
|
258
|
+
self.stream_index = 0
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def call_count(self) -> int:
|
|
262
|
+
"""Number of calls made to this provider."""
|
|
263
|
+
return len(self.calls)
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def last_call(self) -> Optional[dict]:
|
|
267
|
+
"""The most recent call, or None if no calls made."""
|
|
268
|
+
return self.calls[-1] if self.calls else None
|
|
269
|
+
|
|
270
|
+
def get_call(self, index: int) -> Optional[dict]:
|
|
271
|
+
"""Get a specific call by index.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
index: Call index (0-based)
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Call dict or None if index out of range
|
|
278
|
+
"""
|
|
279
|
+
if 0 <= index < len(self.calls):
|
|
280
|
+
return self.calls[index]
|
|
281
|
+
return None
|