codetrail-cli 1.4.1__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.
- codetrail/__init__.py +3 -0
- codetrail/__main__.py +5 -0
- codetrail/agents/__init__.py +4 -0
- codetrail/agents/convert.py +92 -0
- codetrail/agents/export.py +284 -0
- codetrail/agents/instructions.py +42 -0
- codetrail/agents/protocol.py +111 -0
- codetrail/agents/tools.py +185 -0
- codetrail/assistant/__init__.py +161 -0
- codetrail/assistant/claude_code.py +330 -0
- codetrail/assistant/codex.py +260 -0
- codetrail/assistant/environment.py +66 -0
- codetrail/assistant/estimate.py +225 -0
- codetrail/assistant/fake.py +74 -0
- codetrail/assistant/guard.py +80 -0
- codetrail/assistant/guard_hook.py +58 -0
- codetrail/assistant/local.py +245 -0
- codetrail/assistant/local_tools.py +177 -0
- codetrail/assistant/prompts.py +404 -0
- codetrail/assistant/routing.py +86 -0
- codetrail/assistant/runner.py +120 -0
- codetrail/assistant/status.py +186 -0
- codetrail/assistant/usage.py +93 -0
- codetrail/bridge.py +292 -0
- codetrail/cli.py +407 -0
- codetrail/config.py +485 -0
- codetrail/database/__init__.py +60 -0
- codetrail/database/migrations/0001_facts.sql +34 -0
- codetrail/database/migrations/0002_settings.sql +5 -0
- codetrail/database/migrations/0003_learning.sql +34 -0
- codetrail/database/migrations/0004_assistant_usage.sql +24 -0
- codetrail/database/migrations/0005_page_failures.sql +7 -0
- codetrail/database/migrations/0006_metric_values.sql +9 -0
- codetrail/errors.py +5 -0
- codetrail/extract/__init__.py +249 -0
- codetrail/extract/adr.py +98 -0
- codetrail/extract/github_actions.py +140 -0
- codetrail/extract/openapi.py +101 -0
- codetrail/extract/python.py +221 -0
- codetrail/extract/swift.py +204 -0
- codetrail/extract/terraform.py +166 -0
- codetrail/extract/typescript.py +464 -0
- codetrail/facts/__init__.py +112 -0
- codetrail/facts/store.py +209 -0
- codetrail/generate/__init__.py +1 -0
- codetrail/generate/failures.py +31 -0
- codetrail/generate/outline.py +159 -0
- codetrail/generate/revise.py +72 -0
- codetrail/generate/run.py +530 -0
- codetrail/generate/scope.py +135 -0
- codetrail/generate/validate.py +218 -0
- codetrail/guide/__init__.py +147 -0
- codetrail/learn/__init__.py +143 -0
- codetrail/learn/routes.py +139 -0
- codetrail/locales/ar/LC_MESSAGES/codetrail.mo +0 -0
- codetrail/locales/ar/LC_MESSAGES/codetrail.po +2236 -0
- codetrail/locales/codetrail.pot +1942 -0
- codetrail/lock.py +114 -0
- codetrail/metrics/__init__.py +6 -0
- codetrail/metrics/activity.py +96 -0
- codetrail/metrics/churn.py +156 -0
- codetrail/metrics/commits.py +45 -0
- codetrail/metrics/coverage.py +114 -0
- codetrail/metrics/decisions.py +77 -0
- codetrail/metrics/history.py +47 -0
- codetrail/metrics/inventory.py +63 -0
- codetrail/metrics/rationale.py +74 -0
- codetrail/metrics/releases.py +26 -0
- codetrail/metrics/report.py +111 -0
- codetrail/metrics/repository.py +128 -0
- codetrail/metrics/size.py +95 -0
- codetrail/py.typed +0 -0
- codetrail/remove.py +50 -0
- codetrail/repo/__init__.py +4 -0
- codetrail/repo/git.py +56 -0
- codetrail/repo/gitleaks.toml +6 -0
- codetrail/repo/history.py +262 -0
- codetrail/repo/mirror.py +137 -0
- codetrail/repo/refresh.py +41 -0
- codetrail/repo/rules.py +83 -0
- codetrail/repo/secrets.py +145 -0
- codetrail/repo/signal.py +65 -0
- codetrail/repo/source.py +166 -0
- codetrail/search.py +178 -0
- codetrail/server.py +45 -0
- codetrail/system.py +484 -0
- codetrail/update.py +276 -0
- codetrail/web/__init__.py +1 -0
- codetrail/web/app.py +897 -0
- codetrail/web/diagrams.py +414 -0
- codetrail/web/documentation.py +146 -0
- codetrail/web/i18n.py +44 -0
- codetrail/web/navigation.py +127 -0
- codetrail/web/render.py +126 -0
- codetrail/web/repository_view.py +230 -0
- codetrail/web/security.py +171 -0
- codetrail/web/static/brand/favicon.svg +1 -0
- codetrail/web/static/brand/mark.svg +1 -0
- codetrail/web/static/css/base.css +192 -0
- codetrail/web/static/css/components.css +2254 -0
- codetrail/web/static/css/content.css +310 -0
- codetrail/web/static/css/layout.css +410 -0
- codetrail/web/static/css/print.css +42 -0
- codetrail/web/static/css/tokens.css +228 -0
- codetrail/web/static/js/api.js +49 -0
- codetrail/web/static/js/ask.js +182 -0
- codetrail/web/static/js/d2-worker.js +34 -0
- codetrail/web/static/js/d2.js +28 -0
- codetrail/web/static/js/diagram.js +52 -0
- codetrail/web/static/js/documentation.js +24 -0
- codetrail/web/static/js/learning.js +47 -0
- codetrail/web/static/js/main.js +38 -0
- codetrail/web/static/js/outline.js +44 -0
- codetrail/web/static/js/palette.js +211 -0
- codetrail/web/static/js/repository.js +528 -0
- codetrail/web/static/js/resize.js +126 -0
- codetrail/web/static/js/shortcuts.js +87 -0
- codetrail/web/static/js/sidebar.js +194 -0
- codetrail/web/static/js/theme-init.js +27 -0
- codetrail/web/static/js/theme.js +42 -0
- codetrail/web/static/js/update.js +107 -0
- codetrail/web/static/vendor/d2/LICENSE +375 -0
- codetrail/web/static/vendor/d2/LICENSE-elk +263 -0
- codetrail/web/static/vendor/d2/LICENSE-wasm_exec +27 -0
- codetrail/web/static/vendor/d2/VERSION +12 -0
- codetrail/web/static/vendor/d2/d2.wasm +0 -0
- codetrail/web/static/vendor/d2/elk.js +105914 -0
- codetrail/web/static/vendor/d2/wasm_exec.js +656 -0
- codetrail/web/static/vendor/inter/InterVariable-Italic.woff2 +0 -0
- codetrail/web/static/vendor/inter/InterVariable.woff2 +0 -0
- codetrail/web/static/vendor/inter/LICENSE.txt +92 -0
- codetrail/web/static/vendor/inter/VERSION +6 -0
- codetrail/web/static/vendor/noto-sans-arabic/NotoSansArabic-wght.woff2 +0 -0
- codetrail/web/static/vendor/noto-sans-arabic/OFL.txt +93 -0
- codetrail/web/static/vendor/noto-sans-arabic/VERSION +12 -0
- codetrail/web/target_view.py +63 -0
- codetrail/web/templates/answers.html +25 -0
- codetrail/web/templates/area.html +46 -0
- codetrail/web/templates/base.html +37 -0
- codetrail/web/templates/decisions.html +30 -0
- codetrail/web/templates/diagram.html +10 -0
- codetrail/web/templates/digests.html +24 -0
- codetrail/web/templates/documentation.html +247 -0
- codetrail/web/templates/error.html +9 -0
- codetrail/web/templates/estimate.html +48 -0
- codetrail/web/templates/fact.html +29 -0
- codetrail/web/templates/home.html +196 -0
- codetrail/web/templates/layout/ask_panel.html +27 -0
- codetrail/web/templates/layout/header.html +32 -0
- codetrail/web/templates/layout/icons.html +27 -0
- codetrail/web/templates/layout/outline.html +4 -0
- codetrail/web/templates/layout/palette.html +27 -0
- codetrail/web/templates/layout/shortcuts.html +27 -0
- codetrail/web/templates/layout/sidebar.html +79 -0
- codetrail/web/templates/layout/update_dialog.html +18 -0
- codetrail/web/templates/macros.html +13 -0
- codetrail/web/templates/page.html +93 -0
- codetrail/web/templates/progress.html +46 -0
- codetrail/web/templates/repository.html +369 -0
- codetrail/web/templates/search.html +31 -0
- codetrail/web/templates/source.html +15 -0
- codetrail/web/templates/system.html +43 -0
- codetrail/web/templates/system_arrows.html +17 -0
- codetrail/web/templates/system_legend.html +9 -0
- codetrail/web/templates/update_log.html +30 -0
- codetrail/web/trend.py +22 -0
- codetrail_cli-1.4.1.dist-info/METADATA +372 -0
- codetrail_cli-1.4.1.dist-info/RECORD +171 -0
- codetrail_cli-1.4.1.dist-info/WHEEL +4 -0
- codetrail_cli-1.4.1.dist-info/entry_points.txt +3 -0
- codetrail_cli-1.4.1.dist-info/licenses/LICENSE +202 -0
codetrail/update.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""An update: refresh the sources, extract and record the facts, and write the guide, under the target's lock.
|
|
2
|
+
|
|
3
|
+
Facts are recorded before the guide is written: they are true whatever happens to the guide, and a page that isn't
|
|
4
|
+
rewritten stays affected, because that is derived from its front matter (design section 6.8).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import threading
|
|
11
|
+
from collections.abc import Callable, Mapping
|
|
12
|
+
from dataclasses import dataclass, replace
|
|
13
|
+
from datetime import date
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import anyio
|
|
17
|
+
|
|
18
|
+
from codetrail.agents.export import build_export, write_export
|
|
19
|
+
from codetrail.assistant import Assistant
|
|
20
|
+
from codetrail.assistant.estimate import PageToWrite, UpdateEstimate, estimate_update
|
|
21
|
+
from codetrail.assistant.routing import build_assistant
|
|
22
|
+
from codetrail.assistant.status import require_ready
|
|
23
|
+
from codetrail.assistant.usage import UsageLog
|
|
24
|
+
from codetrail.config import (
|
|
25
|
+
ExtractSettings,
|
|
26
|
+
GlobalConfig,
|
|
27
|
+
Paths,
|
|
28
|
+
TargetConfig,
|
|
29
|
+
check_containment,
|
|
30
|
+
load_global,
|
|
31
|
+
load_target,
|
|
32
|
+
model_choice,
|
|
33
|
+
)
|
|
34
|
+
from codetrail.database import connect
|
|
35
|
+
from codetrail.errors import CodetrailError
|
|
36
|
+
from codetrail.extract import Extraction, Extractor, check_facts, run_extractors
|
|
37
|
+
from codetrail.extract.adr import AdrExtractor
|
|
38
|
+
from codetrail.extract.github_actions import GitHubActionsExtractor
|
|
39
|
+
from codetrail.extract.openapi import OpenApiExtractor
|
|
40
|
+
from codetrail.extract.python import PythonExtractor
|
|
41
|
+
from codetrail.extract.swift import SwiftExtractor
|
|
42
|
+
from codetrail.extract.terraform import TerraformExtractor
|
|
43
|
+
from codetrail.extract.typescript import TypeScriptExtractor
|
|
44
|
+
from codetrail.facts import FactDiff, Snapshot
|
|
45
|
+
from codetrail.facts.store import FactStore
|
|
46
|
+
from codetrail.generate.failures import FailedPages
|
|
47
|
+
from codetrail.generate.run import (
|
|
48
|
+
PAGE_ATTEMPTS,
|
|
49
|
+
GenerationContext,
|
|
50
|
+
GenerationResult,
|
|
51
|
+
PlannedWork,
|
|
52
|
+
Progress,
|
|
53
|
+
generate_guide,
|
|
54
|
+
no_progress,
|
|
55
|
+
planned_work,
|
|
56
|
+
)
|
|
57
|
+
from codetrail.guide import GuideRepository
|
|
58
|
+
from codetrail.learn import LearningState
|
|
59
|
+
from codetrail.lock import target_in_use, target_lock
|
|
60
|
+
from codetrail.metrics.history import record_values
|
|
61
|
+
from codetrail.metrics.report import Metric, Value, build_report
|
|
62
|
+
from codetrail.metrics.repository import build_repository_report
|
|
63
|
+
from codetrail.repo.mirror import read_file_at
|
|
64
|
+
from codetrail.repo.refresh import TARGET_IGNORE_FILE, ignore_lines, refresh_while_locked
|
|
65
|
+
from codetrail.repo.rules import ExclusionRules
|
|
66
|
+
from codetrail.repo.secrets import SecretScanner
|
|
67
|
+
from codetrail.repo.source import SourceManifest, allowed_reader
|
|
68
|
+
from codetrail.system import derive
|
|
69
|
+
|
|
70
|
+
STOP_CHECK_SECONDS = 0.2 # how often an update started from the page checks whether the server is stopping
|
|
71
|
+
STOPPED = "The server stopped, so the update stopped; the guide wasn't changed."
|
|
72
|
+
logger = logging.getLogger(__name__)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class UpdateResult:
|
|
77
|
+
manifest: SourceManifest
|
|
78
|
+
snapshot: Snapshot
|
|
79
|
+
diff: FactDiff
|
|
80
|
+
extraction: Extraction
|
|
81
|
+
generation: GenerationResult | None = None
|
|
82
|
+
estimate: UpdateEstimate | None = None
|
|
83
|
+
declined: bool = False # the reader didn't confirm the estimate; facts were refreshed, the guide wasn't touched
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _calls(target: TargetConfig, work: PlannedWork) -> list[tuple[str, str, str, int, int]]:
|
|
87
|
+
"""The update's paid calls for its estimate: kind, provider, model, expected count, maximum count."""
|
|
88
|
+
plan, write, digest = (model_choice(getattr(target.models, kind)) for kind in ("plan", "write", "digest"))
|
|
89
|
+
return [
|
|
90
|
+
("plan", *plan, work.plan_calls, work.plan_calls),
|
|
91
|
+
("write", *write, work.pages_expected, work.page_calls_max),
|
|
92
|
+
("revise", *write, work.revisions_expected, work.revisions_expected * PAGE_ATTEMPTS), # the write model
|
|
93
|
+
("digest", *digest, int(work.digest_expected), int(work.digest_possible)),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def with_system(extraction: Extraction, source: Path, files: Mapping[str, str], extract: ExtractSettings) -> Extraction:
|
|
98
|
+
"""The extraction with the system pass's parts and connections, checked like extracted facts (design 17.3)."""
|
|
99
|
+
found = derive(
|
|
100
|
+
extraction.entities, extraction.relations, files, allowed_reader(source, files, extract.max_file_bytes)
|
|
101
|
+
)
|
|
102
|
+
entities, relations, warnings = check_facts(found.entities, found.relations, extract.max_attribute_chars)
|
|
103
|
+
return replace(
|
|
104
|
+
extraction,
|
|
105
|
+
entities=sorted([*extraction.entities, *entities], key=lambda entity: entity.id),
|
|
106
|
+
relations=sorted([*extraction.relations, *relations], key=lambda relation: relation.key),
|
|
107
|
+
warnings=[*extraction.warnings, *found.warnings, *warnings],
|
|
108
|
+
system=dict(found.counts),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def build_extractors(target: TargetConfig, extract: ExtractSettings | None = None) -> list[Extractor]:
|
|
113
|
+
extract = extract or ExtractSettings()
|
|
114
|
+
available: dict[str, Extractor] = {
|
|
115
|
+
"python": PythonExtractor(),
|
|
116
|
+
"adr": AdrExtractor(target.adr.paths),
|
|
117
|
+
"openapi": OpenApiExtractor(target.openapi.paths),
|
|
118
|
+
"terraform": TerraformExtractor(extract.max_resource_paths),
|
|
119
|
+
"swift": SwiftExtractor(),
|
|
120
|
+
"typescript": TypeScriptExtractor(extract.max_tsconfig_paths),
|
|
121
|
+
"github_actions": GitHubActionsExtractor(extract.max_workflow_steps),
|
|
122
|
+
}
|
|
123
|
+
return [available[name] for name in target.extractors]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def record_metrics(paths: Paths, name: str, settings: GlobalConfig, store: FactStore) -> None:
|
|
127
|
+
"""Writes each documentation metric's and repository statistic's value at the latest snapshot (sections 18.3, 19.4).
|
|
128
|
+
|
|
129
|
+
The trend is secondary to the guide, so a failure here is logged and never fails the update; each report is built
|
|
130
|
+
on its own, so one failing still writes the other's values.
|
|
131
|
+
"""
|
|
132
|
+
snapshot = store.latest_snapshot()
|
|
133
|
+
if snapshot is None:
|
|
134
|
+
return
|
|
135
|
+
today = date.today()
|
|
136
|
+
values: dict[Metric, Value] = {}
|
|
137
|
+
try:
|
|
138
|
+
values |= build_report(paths, name, settings, store, today).values()
|
|
139
|
+
except Exception as error:
|
|
140
|
+
logger.warning("The documentation metrics weren't recorded: %s", error)
|
|
141
|
+
try:
|
|
142
|
+
values |= build_repository_report(paths, name, settings, store, today).values()
|
|
143
|
+
except Exception as error:
|
|
144
|
+
logger.warning("The repository statistics weren't recorded: %s", error)
|
|
145
|
+
if values:
|
|
146
|
+
try:
|
|
147
|
+
record_values(store.connection, snapshot.id, values)
|
|
148
|
+
except Exception as error:
|
|
149
|
+
logger.warning("The metrics' values weren't recorded: %s", error)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def generate_until_stopped(
|
|
153
|
+
context: GenerationContext, claude: Assistant, stop: threading.Event
|
|
154
|
+
) -> GenerationResult:
|
|
155
|
+
"""Writes the guide, cancelled once `stop` is set, so its cleanup runs: each program's process group is killed and
|
|
156
|
+
the guide's uncommitted changes are discarded (design section 15.5)."""
|
|
157
|
+
if stop.is_set(): # a confirmation that raced the stop: no program starts
|
|
158
|
+
raise CodetrailError(STOPPED)
|
|
159
|
+
generation: GenerationResult | None = None
|
|
160
|
+
try:
|
|
161
|
+
async with anyio.create_task_group() as group:
|
|
162
|
+
|
|
163
|
+
async def cancel_when_stopped() -> None:
|
|
164
|
+
while not stop.is_set():
|
|
165
|
+
await anyio.sleep(STOP_CHECK_SECONDS)
|
|
166
|
+
group.cancel_scope.cancel()
|
|
167
|
+
|
|
168
|
+
group.start_soon(cancel_when_stopped)
|
|
169
|
+
generation = await generate_guide(context, claude)
|
|
170
|
+
group.cancel_scope.cancel()
|
|
171
|
+
except BaseExceptionGroup as errors: # only the generation raises, and the group wraps its error
|
|
172
|
+
raise errors.exceptions[0] from None
|
|
173
|
+
if generation is None:
|
|
174
|
+
raise CodetrailError(STOPPED)
|
|
175
|
+
return generation
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def run_update(
|
|
179
|
+
paths: Paths,
|
|
180
|
+
name: str,
|
|
181
|
+
claude: Assistant | None = None,
|
|
182
|
+
facts_only: bool = False,
|
|
183
|
+
confirm: Callable[[UpdateEstimate], bool] | None = None,
|
|
184
|
+
progress: Progress = no_progress,
|
|
185
|
+
stop: threading.Event | None = None,
|
|
186
|
+
retry_failed: bool = False,
|
|
187
|
+
) -> UpdateResult:
|
|
188
|
+
"""Refreshes the sources and facts (free), then, unless `facts_only`, estimates the guide's paid work and asks
|
|
189
|
+
`confirm` before doing it (design section 15.4). An update with a real assistant always needs `confirm`.
|
|
190
|
+
Each step is reported to `progress` as it happens. Setting `stop` (the server stopping) cancels the guide's
|
|
191
|
+
writing. Pages that failed validation wait until their facts change, unless `retry_failed`."""
|
|
192
|
+
with target_in_use(paths, name): # so the target isn't removed during the update
|
|
193
|
+
target = load_target(paths, name)
|
|
194
|
+
check_containment(paths, target.repository) # before the lock creates the data folder
|
|
195
|
+
settings = load_global(paths)
|
|
196
|
+
sign_ins: dict[str, str] = {}
|
|
197
|
+
if claude is None and not facts_only: # before any work: the providers this update uses must be ready
|
|
198
|
+
if confirm is None:
|
|
199
|
+
raise CodetrailError("An update that calls an assistant must show its estimate and be confirmed first.")
|
|
200
|
+
statuses = require_ready(target, settings, kinds=("plan", "write", "digest"))
|
|
201
|
+
sign_ins = {status.provider: status.method for status in statuses}
|
|
202
|
+
data = paths.target_data(name)
|
|
203
|
+
with target_lock(paths, name):
|
|
204
|
+
progress({"step": "facts"})
|
|
205
|
+
manifest = refresh_while_locked(paths, name)
|
|
206
|
+
source = data / "source"
|
|
207
|
+
extraction = run_extractors(
|
|
208
|
+
source, manifest.files, build_extractors(target, settings.extract), settings.extract.max_file_bytes,
|
|
209
|
+
settings.extract.max_attribute_chars,
|
|
210
|
+
) # fmt: skip
|
|
211
|
+
extraction = with_system(extraction, source, manifest.files, settings.extract)
|
|
212
|
+
connection = connect(data / "codetrail.db")
|
|
213
|
+
try:
|
|
214
|
+
store = FactStore(connection)
|
|
215
|
+
|
|
216
|
+
def export() -> None: # the context for coding agents, rebuilt from what this update recorded
|
|
217
|
+
write_export(data / "agents", build_export(name, target, GuideRepository(data / "guide"), store,
|
|
218
|
+
manifest, settings)) # fmt: skip
|
|
219
|
+
|
|
220
|
+
snapshot, diff = store.record(manifest.commit, extraction.entities, extraction.relations)
|
|
221
|
+
progress({"step": "facts_recorded", "changes": len(diff.changed_ids())})
|
|
222
|
+
if facts_only:
|
|
223
|
+
record_metrics(paths, name, settings, store)
|
|
224
|
+
export()
|
|
225
|
+
return UpdateResult(manifest, snapshot, diff, extraction)
|
|
226
|
+
previous = store.previous_snapshot(snapshot)
|
|
227
|
+
mirror = data / "mirror.git"
|
|
228
|
+
rules = ExclusionRules(
|
|
229
|
+
ignore_lines(paths, name, read_file_at(mirror, manifest.commit, TARGET_IGNORE_FILE))
|
|
230
|
+
)
|
|
231
|
+
excluded = manifest.excluded_paths()
|
|
232
|
+
context = GenerationContext(
|
|
233
|
+
target=name,
|
|
234
|
+
guide=GuideRepository(data / "guide"),
|
|
235
|
+
store=store,
|
|
236
|
+
manifest=manifest,
|
|
237
|
+
source_root=source,
|
|
238
|
+
mirror=mirror,
|
|
239
|
+
scanner=SecretScanner(settings.tools),
|
|
240
|
+
visible=lambda path: rules.reason(path) is None and path not in excluded,
|
|
241
|
+
max_pages=target.generation.max_pages_per_update,
|
|
242
|
+
concurrency=target.generation.concurrency,
|
|
243
|
+
max_budget_usd=target.generation.max_budget_usd_per_update,
|
|
244
|
+
max_tokens=target.generation.max_tokens_per_update,
|
|
245
|
+
previous_commit=previous.commit if previous else None,
|
|
246
|
+
diff=diff,
|
|
247
|
+
learned=LearningState(connection).learned_page_ids(),
|
|
248
|
+
usage=UsageLog(connection, settings.prices),
|
|
249
|
+
progress=progress,
|
|
250
|
+
failed_pages=FailedPages(connection),
|
|
251
|
+
retry_failed=retry_failed,
|
|
252
|
+
revise_max_changes=target.generation.revise_max_changes,
|
|
253
|
+
max_brief_words=settings.agents.max_brief_words,
|
|
254
|
+
)
|
|
255
|
+
if confirm is not None:
|
|
256
|
+
work = planned_work(context)
|
|
257
|
+
estimate = estimate_update(connection, _calls(target, work), settings.estimates, settings.prices,
|
|
258
|
+
sign_ins, target.generation.max_budget_usd_per_update,
|
|
259
|
+
target.generation.max_tokens_per_update,
|
|
260
|
+
[PageToWrite(page.entry.title, page.reason, page.changed_now,
|
|
261
|
+
bool(page.changes)) for page in work.pages],
|
|
262
|
+
[entry.title for entry in work.skipped]) # fmt: skip
|
|
263
|
+
if not confirm(estimate):
|
|
264
|
+
record_metrics(paths, name, settings, store)
|
|
265
|
+
export()
|
|
266
|
+
return UpdateResult(manifest, snapshot, diff, extraction, None, estimate, declined=True)
|
|
267
|
+
writer = claude or build_assistant(source, settings, target)
|
|
268
|
+
if stop is None:
|
|
269
|
+
generation = anyio.run(generate_guide, context, writer)
|
|
270
|
+
else:
|
|
271
|
+
generation = anyio.run(generate_until_stopped, context, writer, stop)
|
|
272
|
+
record_metrics(paths, name, settings, store)
|
|
273
|
+
export()
|
|
274
|
+
finally:
|
|
275
|
+
connection.close()
|
|
276
|
+
return UpdateResult(manifest, snapshot, diff, extraction, generation)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The local page: FastAPI routes, templates, diagrams and the security model (design section 7)."""
|