codespy-ai 0.2.0__tar.gz → 0.2.2__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.
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/PKG-INFO +33 -2
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/README.md +32 -1
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/pyproject.toml +1 -1
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/__init__.py +1 -1
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/reviewer.py +46 -26
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/config.py +16 -25
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/config_llm.py +40 -1
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/LICENSE +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/cost_tracker.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/dspy_config.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/code_and_doc_reviewer.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/deduplicator.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/helpers.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/scope_identifier.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/supply_chain_auditor.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/reporters/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/reporters/base.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/reporters/git.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/reporters/stdout.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/cli.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/config_dspy.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/config_git.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/config_io.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/cyber/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/cyber/osv/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/cyber/osv/client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/cyber/osv/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/cyber/osv/server.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/filesystem/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/filesystem/client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/filesystem/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/filesystem/server.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/base.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/github_client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/gitlab_client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/git/server.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/mcp_utils.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/ripgrep/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/ripgrep/client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/ripgrep/server.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/base_extractor.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/go.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/java.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/javascript.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/kotlin.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/objc.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/python.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/rust.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/swift.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/terraform.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/parser.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/server.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/web/__init__.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/web/client.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/web/models.py +0 -0
- {codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/web/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codespy-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Code review agent powered by DSPy
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -247,13 +247,44 @@ docker run --rm \
|
|
|
247
247
|
-e GITHUB_TOKEN=$GITHUB_TOKEN \
|
|
248
248
|
-e DEFAULT_MODEL=claude-opus-4-6 \
|
|
249
249
|
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
|
|
250
|
-
ghcr.io/khezen/codespy:0.1
|
|
250
|
+
ghcr.io/khezen/codespy:0.2.1 review https://github.com/owner/repo/pull/123
|
|
251
251
|
```
|
|
252
252
|
|
|
253
253
|
### GitHub Action
|
|
254
254
|
|
|
255
255
|
Add CodeSpy to your repository for automatic PR reviews:
|
|
256
256
|
|
|
257
|
+
**Trigger on `@codespy review` comment:**
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
# .github/workflows/codespy-review.yml
|
|
261
|
+
name: CodeSpy Code Review
|
|
262
|
+
|
|
263
|
+
on:
|
|
264
|
+
issue_comment:
|
|
265
|
+
types: [created]
|
|
266
|
+
|
|
267
|
+
jobs:
|
|
268
|
+
review:
|
|
269
|
+
# Only run on PR comments containing '@codespy review'
|
|
270
|
+
if: |
|
|
271
|
+
github.event.issue.pull_request &&
|
|
272
|
+
contains(github.event.comment.body, '@codespy review')
|
|
273
|
+
runs-on: ubuntu-latest
|
|
274
|
+
permissions:
|
|
275
|
+
contents: read
|
|
276
|
+
pull-requests: write
|
|
277
|
+
|
|
278
|
+
steps:
|
|
279
|
+
- name: Run CodeSpy Review
|
|
280
|
+
uses: khezen/codespy@v1
|
|
281
|
+
with:
|
|
282
|
+
model: 'claude-opus-4-6'
|
|
283
|
+
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Trigger automatically on every PR:**
|
|
287
|
+
|
|
257
288
|
```yaml
|
|
258
289
|
# .github/workflows/codespy-review.yml
|
|
259
290
|
name: CodeSpy Code Review
|
|
@@ -196,13 +196,44 @@ docker run --rm \
|
|
|
196
196
|
-e GITHUB_TOKEN=$GITHUB_TOKEN \
|
|
197
197
|
-e DEFAULT_MODEL=claude-opus-4-6 \
|
|
198
198
|
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
|
|
199
|
-
ghcr.io/khezen/codespy:0.1
|
|
199
|
+
ghcr.io/khezen/codespy:0.2.1 review https://github.com/owner/repo/pull/123
|
|
200
200
|
```
|
|
201
201
|
|
|
202
202
|
### GitHub Action
|
|
203
203
|
|
|
204
204
|
Add CodeSpy to your repository for automatic PR reviews:
|
|
205
205
|
|
|
206
|
+
**Trigger on `@codespy review` comment:**
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
# .github/workflows/codespy-review.yml
|
|
210
|
+
name: CodeSpy Code Review
|
|
211
|
+
|
|
212
|
+
on:
|
|
213
|
+
issue_comment:
|
|
214
|
+
types: [created]
|
|
215
|
+
|
|
216
|
+
jobs:
|
|
217
|
+
review:
|
|
218
|
+
# Only run on PR comments containing '@codespy review'
|
|
219
|
+
if: |
|
|
220
|
+
github.event.issue.pull_request &&
|
|
221
|
+
contains(github.event.comment.body, '@codespy review')
|
|
222
|
+
runs-on: ubuntu-latest
|
|
223
|
+
permissions:
|
|
224
|
+
contents: read
|
|
225
|
+
pull-requests: write
|
|
226
|
+
|
|
227
|
+
steps:
|
|
228
|
+
- name: Run CodeSpy Review
|
|
229
|
+
uses: khezen/codespy@v1
|
|
230
|
+
with:
|
|
231
|
+
model: 'claude-opus-4-6'
|
|
232
|
+
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Trigger automatically on every PR:**
|
|
236
|
+
|
|
206
237
|
```yaml
|
|
207
238
|
# .github/workflows/codespy-review.yml
|
|
208
239
|
name: CodeSpy Code Review
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Main review pipeline that orchestrates all review modules."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import logging
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
@@ -100,6 +101,40 @@ class ReviewPipeline(dspy.Module):
|
|
|
100
101
|
owner_path = mr.repo_owner.replace("/", "_")
|
|
101
102
|
return cache_dir / owner_path / mr.repo_name
|
|
102
103
|
|
|
104
|
+
async def _run_review_modules(
|
|
105
|
+
self,
|
|
106
|
+
scopes: list,
|
|
107
|
+
repo_path: Path,
|
|
108
|
+
module_names: list[str],
|
|
109
|
+
) -> list[Issue]:
|
|
110
|
+
"""Run review modules concurrently in a single event loop.
|
|
111
|
+
|
|
112
|
+
Uses asyncio.gather instead of dspy.Parallel to avoid the
|
|
113
|
+
multi-thread + multi-event-loop conflict that causes
|
|
114
|
+
'cannot schedule new futures after shutdown' errors.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
scopes: Identified scopes with changed files
|
|
118
|
+
repo_path: Path to the cloned repository
|
|
119
|
+
module_names: Names of modules (for error logging)
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Aggregated list of issues from all modules
|
|
123
|
+
"""
|
|
124
|
+
tasks = [
|
|
125
|
+
self.code_and_doc_reviewer.aforward(scopes=scopes, repo_path=repo_path),
|
|
126
|
+
self.supply_chain_auditor.aforward(scopes=scopes, repo_path=repo_path),
|
|
127
|
+
]
|
|
128
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
129
|
+
|
|
130
|
+
all_issues: list[Issue] = []
|
|
131
|
+
for i, result in enumerate(results):
|
|
132
|
+
if isinstance(result, Exception):
|
|
133
|
+
logger.error(f"{module_names[i]} failed: {result}")
|
|
134
|
+
elif result is not None:
|
|
135
|
+
all_issues.extend(result)
|
|
136
|
+
return all_issues
|
|
137
|
+
|
|
103
138
|
def forward(self, mr_url: str, verify_model: bool = True) -> ReviewResult:
|
|
104
139
|
"""Run the complete review pipeline on a merge request."""
|
|
105
140
|
self.cost_tracker.reset()
|
|
@@ -122,28 +157,13 @@ class ReviewPipeline(dspy.Module):
|
|
|
122
157
|
if manifest.dependencies_changed:
|
|
123
158
|
logger.info(f" Dependencies changed: Yes")
|
|
124
159
|
|
|
125
|
-
#
|
|
126
|
-
all_issues: list[Issue] = []
|
|
127
|
-
exec_pairs = [
|
|
128
|
-
(self.code_and_doc_reviewer, {"scopes": scopes, "repo_path": repo_path}),
|
|
129
|
-
(self.supply_chain_auditor, {"scopes": scopes, "repo_path": repo_path}),
|
|
130
|
-
]
|
|
160
|
+
# Run review modules concurrently via asyncio.gather
|
|
131
161
|
module_names = ["code_and_doc_reviewer", "supply_chain_auditor"]
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
results, failed_examples, exceptions = parallel(exec_pairs)
|
|
138
|
-
# Log any failures
|
|
139
|
-
for i, (failed, exc) in enumerate(zip(failed_examples, exceptions)):
|
|
140
|
-
if failed is not None and exc is not None:
|
|
141
|
-
logger.error(f"{module_names[i]} failed: {exc}")
|
|
142
|
-
# Aggregate successful results
|
|
143
|
-
for result in results:
|
|
144
|
-
if result is not None:
|
|
145
|
-
all_issues.extend(result)
|
|
146
|
-
logger.info(f"Found {len(all_issues)} issues before deduplication")
|
|
162
|
+
logger.info(f"Running review modules concurrently: {', '.join(module_names)}...")
|
|
163
|
+
all_issues = asyncio.run(
|
|
164
|
+
self._run_review_modules(scopes, repo_path, module_names)
|
|
165
|
+
)
|
|
166
|
+
logger.info(f"Found {len(all_issues)} issues before deduplication")
|
|
147
167
|
|
|
148
168
|
# Deduplicate issues across reviewers (deduplicator checks if enabled internally)
|
|
149
169
|
if len(all_issues) > 1:
|
|
@@ -178,7 +198,7 @@ class ReviewPipeline(dspy.Module):
|
|
|
178
198
|
recommendation = "NEEDS_DISCUSSION" if all_issues else "APPROVE"
|
|
179
199
|
# Collect per-signature statistics
|
|
180
200
|
signature_stats_list = self._collect_signature_stats()
|
|
181
|
-
|
|
201
|
+
|
|
182
202
|
return ReviewResult(
|
|
183
203
|
mr_number=mr.number,
|
|
184
204
|
mr_title=mr.title,
|
|
@@ -197,13 +217,13 @@ class ReviewPipeline(dspy.Module):
|
|
|
197
217
|
|
|
198
218
|
def _collect_signature_stats(self) -> list[SignatureStatsResult]:
|
|
199
219
|
"""Collect statistics from all signatures that executed.
|
|
200
|
-
|
|
220
|
+
|
|
201
221
|
Returns:
|
|
202
222
|
List of SignatureStatsResult for each signature that ran
|
|
203
223
|
"""
|
|
204
224
|
stats_list: list[SignatureStatsResult] = []
|
|
205
225
|
all_signature_stats = self.cost_tracker.get_all_signature_stats()
|
|
206
|
-
|
|
226
|
+
|
|
207
227
|
for signature_name, stats in all_signature_stats.items():
|
|
208
228
|
stats_list.append(SignatureStatsResult(
|
|
209
229
|
name=signature_name,
|
|
@@ -212,5 +232,5 @@ class ReviewPipeline(dspy.Module):
|
|
|
212
232
|
call_count=stats.call_count,
|
|
213
233
|
duration_seconds=stats.duration_seconds,
|
|
214
234
|
))
|
|
215
|
-
|
|
216
|
-
return stats_list
|
|
235
|
+
|
|
236
|
+
return stats_list
|
|
@@ -330,32 +330,23 @@ class Settings(BaseSettings):
|
|
|
330
330
|
|
|
331
331
|
@model_validator(mode="after")
|
|
332
332
|
def sync_llm_settings(self) -> "Settings":
|
|
333
|
-
"""Sync LLM settings between nested and flat fields.
|
|
334
|
-
# Sync from nested to flat
|
|
335
|
-
if self.llm.openai_api_key:
|
|
336
|
-
self.openai_api_key = self.llm.openai_api_key
|
|
337
|
-
if self.llm.anthropic_api_key:
|
|
338
|
-
self.anthropic_api_key = self.llm.anthropic_api_key
|
|
339
|
-
if self.llm.gemini_api_key:
|
|
340
|
-
self.gemini_api_key = self.llm.gemini_api_key
|
|
341
|
-
if self.llm.aws_region:
|
|
342
|
-
self.aws_region = self.llm.aws_region
|
|
343
|
-
if self.llm.aws_access_key_id:
|
|
344
|
-
self.aws_access_key_id = self.llm.aws_access_key_id
|
|
345
|
-
if self.llm.aws_secret_access_key:
|
|
346
|
-
self.aws_secret_access_key = self.llm.aws_secret_access_key
|
|
347
|
-
|
|
348
|
-
# Sync from flat to nested
|
|
349
|
-
if self.openai_api_key and not self.llm.openai_api_key:
|
|
350
|
-
self.llm.openai_api_key = self.openai_api_key
|
|
351
|
-
if self.anthropic_api_key and not self.llm.anthropic_api_key:
|
|
352
|
-
self.llm.anthropic_api_key = self.anthropic_api_key
|
|
353
|
-
if self.gemini_api_key and not self.llm.gemini_api_key:
|
|
354
|
-
self.llm.gemini_api_key = self.gemini_api_key
|
|
355
|
-
|
|
356
|
-
# Sync prompt caching setting
|
|
357
|
-
self.enable_prompt_caching = self.llm.enable_prompt_caching
|
|
333
|
+
"""Sync LLM settings between nested and flat fields.
|
|
358
334
|
|
|
335
|
+
Delegates to LLMConfig.sync_from_flat which enforces:
|
|
336
|
+
env vars (flat fields from pydantic-settings) > YAML/nested > defaults.
|
|
337
|
+
"""
|
|
338
|
+
merged = self.llm.sync_from_flat(
|
|
339
|
+
openai_api_key=self.openai_api_key,
|
|
340
|
+
anthropic_api_key=self.anthropic_api_key,
|
|
341
|
+
gemini_api_key=self.gemini_api_key,
|
|
342
|
+
aws_region=self.aws_region,
|
|
343
|
+
aws_access_key_id=self.aws_access_key_id,
|
|
344
|
+
aws_secret_access_key=self.aws_secret_access_key,
|
|
345
|
+
)
|
|
346
|
+
# Propagate merged values back to flat fields
|
|
347
|
+
for field, value in merged.items():
|
|
348
|
+
if value is not None:
|
|
349
|
+
setattr(self, field, value)
|
|
359
350
|
return self
|
|
360
351
|
|
|
361
352
|
@model_validator(mode="after")
|
|
@@ -268,4 +268,43 @@ class LLMConfig(BaseModel):
|
|
|
268
268
|
azure_api_version: str | None = None
|
|
269
269
|
|
|
270
270
|
# Enable provider-side prompt caching (Anthropic, OpenAI, Bedrock, etc.)
|
|
271
|
-
enable_prompt_caching: bool = True
|
|
271
|
+
enable_prompt_caching: bool = True
|
|
272
|
+
|
|
273
|
+
def sync_from_flat(
|
|
274
|
+
self,
|
|
275
|
+
*,
|
|
276
|
+
openai_api_key: str | None = None,
|
|
277
|
+
anthropic_api_key: str | None = None,
|
|
278
|
+
gemini_api_key: str | None = None,
|
|
279
|
+
aws_region: str | None = None,
|
|
280
|
+
aws_access_key_id: str | None = None,
|
|
281
|
+
aws_secret_access_key: str | None = None,
|
|
282
|
+
) -> dict[str, object]:
|
|
283
|
+
"""Sync flat settings into this LLMConfig and return values to propagate back.
|
|
284
|
+
|
|
285
|
+
Priority: flat fields (from env vars via pydantic-settings) > nested defaults.
|
|
286
|
+
Returns dict of field values that should be set on the parent Settings.
|
|
287
|
+
"""
|
|
288
|
+
# Step 1: Flat → nested (env vars win over nested defaults)
|
|
289
|
+
if openai_api_key:
|
|
290
|
+
self.openai_api_key = openai_api_key
|
|
291
|
+
if anthropic_api_key:
|
|
292
|
+
self.anthropic_api_key = anthropic_api_key
|
|
293
|
+
if gemini_api_key:
|
|
294
|
+
self.gemini_api_key = gemini_api_key
|
|
295
|
+
if aws_region:
|
|
296
|
+
self.aws_region = aws_region
|
|
297
|
+
if aws_access_key_id:
|
|
298
|
+
self.aws_access_key_id = aws_access_key_id
|
|
299
|
+
if aws_secret_access_key:
|
|
300
|
+
self.aws_secret_access_key = aws_secret_access_key
|
|
301
|
+
|
|
302
|
+
# Step 2: Return merged values (nested fills gaps where flat is not set)
|
|
303
|
+
return {
|
|
304
|
+
"openai_api_key": openai_api_key or self.openai_api_key,
|
|
305
|
+
"anthropic_api_key": anthropic_api_key or self.anthropic_api_key,
|
|
306
|
+
"gemini_api_key": gemini_api_key or self.gemini_api_key,
|
|
307
|
+
"aws_access_key_id": aws_access_key_id or self.aws_access_key_id,
|
|
308
|
+
"aws_secret_access_key": aws_secret_access_key or self.aws_secret_access_key,
|
|
309
|
+
"enable_prompt_caching": self.enable_prompt_caching,
|
|
310
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/code_and_doc_reviewer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/scope_identifier.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/agents/reviewer/modules/supply_chain_auditor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/base_extractor.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/java.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/javascript.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/kotlin.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/objc.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/python.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/rust.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/swift.py
RENAMED
|
File without changes
|
{codespy_ai-0.2.0 → codespy_ai-0.2.2}/src/codespy/tools/parsers/treesitter/extractors/terraform.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|