agent-apprenticeship 0.1.6 → 0.1.7
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.
- package/bin/agent-apprenticeship.js +85 -37
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/agent_apprenticeship_trace/__init__.py +1 -1
- package/src/agent_apprenticeship_trace/actual_outputs_normalizer.py +104 -28
- package/src/agent_apprenticeship_trace/apprentice_adapters.py +3 -1
- package/src/agent_apprenticeship_trace/artifact_resolver.py +6 -0
- package/src/agent_apprenticeship_trace/bundle_exporter.py +132 -2
- package/src/agent_apprenticeship_trace/cli.py +1171 -262
- package/src/agent_apprenticeship_trace/codex_runner.py +89 -6
- package/src/agent_apprenticeship_trace/config.py +251 -32
- package/src/agent_apprenticeship_trace/ecosystem_r2.py +515 -0
- package/src/agent_apprenticeship_trace/env.py +5 -5
- package/src/agent_apprenticeship_trace/evaluator.py +127 -11
- package/src/agent_apprenticeship_trace/grader.py +12 -11
- package/src/agent_apprenticeship_trace/learning.py +270 -1
- package/src/agent_apprenticeship_trace/llm_output_normalizer.py +8 -7
- package/src/agent_apprenticeship_trace/loop.py +103 -4
- package/src/agent_apprenticeship_trace/loop_review.py +686 -0
- package/src/agent_apprenticeship_trace/mentor_checkpoints.py +49 -21
- package/src/agent_apprenticeship_trace/openai_structured.py +84 -14
- package/src/agent_apprenticeship_trace/package_exporter.py +1 -1
- package/src/agent_apprenticeship_trace/progress.py +2 -1
- package/src/agent_apprenticeship_trace/public_run.py +369 -57
- package/src/agent_apprenticeship_trace/public_sanitizer.py +37 -1
- package/src/agent_apprenticeship_trace/rubric_generation.py +11 -10
- package/src/agent_apprenticeship_trace/schemas.py +378 -3
- package/src/agent_apprenticeship_trace/task_intake.py +43 -9
- package/src/agent_apprenticeship_trace/tdo.py +2304 -0
- package/src/agent_apprenticeship_trace/trace_normalizer.py +351 -17
- package/src/agent_apprenticeship_trace/trace_prompt.py +38 -11
- package/src/agent_apprenticeship_trace/training_signals.py +1 -1
- package/src/agent_apprenticeship_trace/verifier.py +12 -6
|
@@ -43,6 +43,33 @@ def _attempt_summary(pkg: Path | None) -> dict[str, Any]:
|
|
|
43
43
|
return {"attempts": len(attempts), "traced_steps": traced_steps, "artifacts": artifacts[:50]}
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def _mark_source_artifact_audited(pkg: Path | None, *, source: str, mode: str, input_source: str, stages: set[str]) -> None:
|
|
47
|
+
if not pkg:
|
|
48
|
+
return
|
|
49
|
+
updates = {
|
|
50
|
+
"mentor_audited": True,
|
|
51
|
+
"mentor_audit_source": source,
|
|
52
|
+
"audit_status": f"{mode}_audit_complete",
|
|
53
|
+
"checkpoint_input_source": input_source,
|
|
54
|
+
}
|
|
55
|
+
targets: list[Path] = []
|
|
56
|
+
if "task_intake" in stages:
|
|
57
|
+
targets.append(pkg / "task" / "task_intake_spec.json")
|
|
58
|
+
if "rubric" in stages:
|
|
59
|
+
targets.append(pkg / "rubric" / "rubric.json")
|
|
60
|
+
for path in targets:
|
|
61
|
+
if not path.exists():
|
|
62
|
+
continue
|
|
63
|
+
try:
|
|
64
|
+
data = read_json(path)
|
|
65
|
+
md = dict(data.get("metadata_json") or {})
|
|
66
|
+
md.update(updates)
|
|
67
|
+
data["metadata_json"] = md
|
|
68
|
+
write_json(path, data)
|
|
69
|
+
except Exception:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
|
|
46
73
|
def _truthy_env(name: str) -> bool:
|
|
47
74
|
return os.getenv(name, "").strip().lower() in {"1", "true", "yes", "on"}
|
|
48
75
|
|
|
@@ -81,7 +108,7 @@ def _parse_score(value: str, default: float) -> float:
|
|
|
81
108
|
return max(0.0, min(1.0, score))
|
|
82
109
|
|
|
83
110
|
|
|
84
|
-
APPROVE_WORDS = {"accept", "approve", "approved", "yes", "y", "ok", "okay"}
|
|
111
|
+
APPROVE_WORDS = {"accept", "approve", "approved", "confirm", "confirmed", "yes", "y", "ok", "okay"}
|
|
85
112
|
|
|
86
113
|
|
|
87
114
|
def _is_approve(value: str) -> bool:
|
|
@@ -103,42 +130,42 @@ def _collect_interactive_inputs(
|
|
|
103
130
|
summary: dict[str, Any],
|
|
104
131
|
stages: set[str],
|
|
105
132
|
) -> dict[str, Any]:
|
|
106
|
-
label = "Expert-led"
|
|
133
|
+
label = "Expert-led"
|
|
107
134
|
default_passed = "pass" if status.get("task_status") == "completed" else "fail"
|
|
108
135
|
default_score = "1.0" if default_passed == "pass" else "0.0"
|
|
109
136
|
collected: dict[str, Any] = {}
|
|
110
137
|
if "task_intake" in stages:
|
|
111
138
|
print(f"{label} checkpoint: task intake")
|
|
112
|
-
intake_decision = _prompt("
|
|
139
|
+
intake_decision = _prompt("Confirm or edit task intake", "confirm")
|
|
113
140
|
if _is_edit(intake_decision):
|
|
114
141
|
edited_title = _prompt("Edited title, optional", "")
|
|
115
142
|
intake_notes = _prompt("Task intake notes, optional", "")
|
|
116
143
|
decision = "edited"
|
|
117
144
|
else:
|
|
118
145
|
edited_title = ""
|
|
119
|
-
intake_notes = "
|
|
120
|
-
decision = "
|
|
146
|
+
intake_notes = "Confirmed as-is." if _is_approve(intake_decision) else f"Confirmed as-is. Input: {intake_decision}"
|
|
147
|
+
decision = "confirmed"
|
|
121
148
|
collected["task_intake"] = {
|
|
122
149
|
"decision": decision,
|
|
123
150
|
"edited_title": edited_title or None,
|
|
124
|
-
"notes": intake_notes or "
|
|
151
|
+
"notes": intake_notes or "Confirmed as-is.",
|
|
125
152
|
}
|
|
126
153
|
|
|
127
154
|
if "rubric" in stages:
|
|
128
155
|
print(f"{label} checkpoint: rubric")
|
|
129
|
-
rubric_decision = _prompt("
|
|
156
|
+
rubric_decision = _prompt("Confirm or edit rubric", "confirm")
|
|
130
157
|
if _is_edit(rubric_decision):
|
|
131
158
|
additional_criterion = _prompt("Add rubric criterion, optional", "")
|
|
132
159
|
rubric_notes = _prompt("Rubric notes, optional", "")
|
|
133
160
|
rubric_choice = "edited"
|
|
134
161
|
else:
|
|
135
162
|
additional_criterion = ""
|
|
136
|
-
rubric_notes = "Rubric
|
|
137
|
-
rubric_choice = "
|
|
163
|
+
rubric_notes = "Rubric confirmed." if _is_approve(rubric_decision) else f"Rubric confirmed. Input: {rubric_decision}"
|
|
164
|
+
rubric_choice = "confirmed"
|
|
138
165
|
collected["rubric"] = {
|
|
139
166
|
"decision": rubric_choice,
|
|
140
167
|
"additional_criteria": [additional_criterion] if additional_criterion else [],
|
|
141
|
-
"notes": rubric_notes or "Rubric
|
|
168
|
+
"notes": rubric_notes or "Rubric confirmed.",
|
|
142
169
|
}
|
|
143
170
|
|
|
144
171
|
if "evaluation" in stages:
|
|
@@ -168,12 +195,12 @@ def _collect_interactive_inputs(
|
|
|
168
195
|
|
|
169
196
|
if "final_approval" in stages:
|
|
170
197
|
print(f"{label} checkpoint: final bundle approval")
|
|
171
|
-
final_decision = _prompt("
|
|
198
|
+
final_decision = _prompt("Confirm final Experience Compilation or hold", "confirm").strip().lower()
|
|
172
199
|
final_notes = _prompt("Final approval notes, optional", "")
|
|
173
200
|
collected["final_approval"] = {
|
|
174
201
|
"approved_for_local_bundle": _is_approve(final_decision) or final_decision.startswith("approve"),
|
|
175
|
-
"decision": "
|
|
176
|
-
"notes": final_notes or "Final
|
|
202
|
+
"decision": "confirmed" if (_is_approve(final_decision) or final_decision.startswith("approve") or final_decision.startswith("confirm")) else "held",
|
|
203
|
+
"notes": final_notes or "Final Experience Compilation checkpoint completed.",
|
|
177
204
|
}
|
|
178
205
|
|
|
179
206
|
return collected
|
|
@@ -273,8 +300,8 @@ def write_mentor_checkpoints(
|
|
|
273
300
|
"checkpoint_type": "task_intake",
|
|
274
301
|
"title": intake_input.get("edited_title") or status.get("task_title"),
|
|
275
302
|
"instruction": session.get("task_instruction"),
|
|
276
|
-
"decision": intake_input.get("decision") or "
|
|
277
|
-
"notes": intake_input.get("notes") or ("
|
|
303
|
+
"decision": intake_input.get("decision") or "confirmed",
|
|
304
|
+
"notes": intake_input.get("notes") or ("Confirmed as-is." if auto_approve else "Human expert task-intake audit recorded."),
|
|
278
305
|
"checkpoint_input_source": input_source,
|
|
279
306
|
},
|
|
280
307
|
preserve_interactive=preserve_interactive,
|
|
@@ -290,9 +317,9 @@ def write_mentor_checkpoints(
|
|
|
290
317
|
"rubric/worker_visible_rubric.md",
|
|
291
318
|
"rubric/rubric.json",
|
|
292
319
|
],
|
|
293
|
-
"decision": rubric_input.get("decision") or "
|
|
320
|
+
"decision": rubric_input.get("decision") or "confirmed",
|
|
294
321
|
"additional_criteria": rubric_input.get("additional_criteria") or [],
|
|
295
|
-
"notes": rubric_input.get("notes") or ("
|
|
322
|
+
"notes": rubric_input.get("notes") or ("Apprentice-generated rubric confirmed." if auto_approve else "Human expert rubric audit recorded."),
|
|
296
323
|
"checkpoint_input_source": input_source,
|
|
297
324
|
},
|
|
298
325
|
preserve_interactive=preserve_interactive,
|
|
@@ -340,15 +367,16 @@ def write_mentor_checkpoints(
|
|
|
340
367
|
"checkpoint_type": "final_approval",
|
|
341
368
|
"approved_for_local_bundle": final_input.get("approved_for_local_bundle", True),
|
|
342
369
|
"contribution_bundle_path": status.get("contribution_bundle_path"),
|
|
343
|
-
"decision": final_input.get("decision") or "
|
|
344
|
-
"notes": final_input.get("notes") or "Final
|
|
370
|
+
"decision": final_input.get("decision") or "confirmed",
|
|
371
|
+
"notes": final_input.get("notes") or "Final Experience Compilation checkpoint recorded.",
|
|
345
372
|
"checkpoint_input_source": input_source,
|
|
346
373
|
},
|
|
347
374
|
preserve_interactive=preserve_interactive,
|
|
348
375
|
)
|
|
349
376
|
(root / "README.md").write_text(
|
|
350
377
|
"# Mentor Checkpoints\n\n"
|
|
351
|
-
"These files record expert-led or hybrid human
|
|
352
|
-
"They are local
|
|
378
|
+
"These files record expert-led or hybrid human audit checkpoints for the apprenticeship session. "
|
|
379
|
+
"They are local audit artifacts and do not contain raw provider secrets.\n"
|
|
353
380
|
)
|
|
381
|
+
_mark_source_artifact_audited(pkg, source=source, mode=mode, input_source=input_source, stages=enabled_stages)
|
|
354
382
|
return root
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
import importlib, json, os, re, sys, time, urllib.error, urllib.parse, urllib.request
|
|
2
|
+
import importlib, json, os, re, signal, sys, threading, time, urllib.error, urllib.parse, urllib.request
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
from pydantic import BaseModel
|
|
@@ -150,6 +150,69 @@ def _provider_max_output_tokens() -> int:
|
|
|
150
150
|
value = 2048
|
|
151
151
|
return max(256, min(value, 4096))
|
|
152
152
|
|
|
153
|
+
|
|
154
|
+
def _provider_request_timeout_seconds() -> int:
|
|
155
|
+
settings = get_settings()
|
|
156
|
+
raw = os.getenv("AA_ROLE_TIMEOUT_SECONDS") or os.getenv("AA_MODEL_ROLE_TIMEOUT_SECONDS")
|
|
157
|
+
if raw:
|
|
158
|
+
try:
|
|
159
|
+
return max(10, min(int(raw), settings.task_timeout_seconds))
|
|
160
|
+
except ValueError:
|
|
161
|
+
pass
|
|
162
|
+
return max(10, min(30, settings.task_timeout_seconds))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _provider_request_retry_count() -> int:
|
|
166
|
+
raw = os.getenv("AA_ROLE_RETRY_COUNT") or os.getenv("AA_MODEL_ROLE_RETRY_COUNT")
|
|
167
|
+
if raw:
|
|
168
|
+
try:
|
|
169
|
+
return max(1, min(int(raw), 3))
|
|
170
|
+
except ValueError:
|
|
171
|
+
pass
|
|
172
|
+
return 1
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _provider_wall_timeout_seconds() -> int:
|
|
176
|
+
settings = get_settings()
|
|
177
|
+
raw = os.getenv("AA_ROLE_WALL_TIMEOUT_SECONDS") or os.getenv("AA_MODEL_ROLE_WALL_TIMEOUT_SECONDS")
|
|
178
|
+
if raw:
|
|
179
|
+
try:
|
|
180
|
+
return max(1, min(int(raw), settings.task_timeout_seconds))
|
|
181
|
+
except ValueError:
|
|
182
|
+
pass
|
|
183
|
+
return max(12, min(_provider_request_timeout_seconds() + 2, settings.task_timeout_seconds))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _with_wall_timeout(label: str, fn):
|
|
187
|
+
timeout = _provider_wall_timeout_seconds()
|
|
188
|
+
if threading.current_thread() is not threading.main_thread() or not hasattr(signal, "setitimer"):
|
|
189
|
+
return fn()
|
|
190
|
+
old_handler = signal.getsignal(signal.SIGALRM)
|
|
191
|
+
old_timer = signal.setitimer(signal.ITIMER_REAL, 0)
|
|
192
|
+
|
|
193
|
+
def _raise_timeout(_signum, _frame):
|
|
194
|
+
raise TimeoutError(f"{label} timed out after {timeout} seconds.")
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
signal.signal(signal.SIGALRM, _raise_timeout)
|
|
198
|
+
signal.setitimer(signal.ITIMER_REAL, timeout)
|
|
199
|
+
return fn()
|
|
200
|
+
finally:
|
|
201
|
+
signal.setitimer(signal.ITIMER_REAL, 0)
|
|
202
|
+
signal.signal(signal.SIGALRM, old_handler)
|
|
203
|
+
if old_timer and old_timer[0] > 0:
|
|
204
|
+
signal.setitimer(signal.ITIMER_REAL, old_timer[0], old_timer[1])
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _construct_openai_client(mod: Any, kwargs: dict[str, Any]) -> Any:
|
|
208
|
+
try:
|
|
209
|
+
return mod.OpenAI(**kwargs)
|
|
210
|
+
except TypeError as exc:
|
|
211
|
+
if "timeout" not in kwargs or "timeout" not in str(exc):
|
|
212
|
+
raise
|
|
213
|
+
return mod.OpenAI(**{k: v for k, v in kwargs.items() if k != "timeout"})
|
|
214
|
+
|
|
215
|
+
|
|
153
216
|
def get_model_provider_status(provider_id: str | None = None) -> dict[str, Any]:
|
|
154
217
|
provider_id = _configured_provider(provider_id)
|
|
155
218
|
settings = get_settings()
|
|
@@ -182,7 +245,7 @@ def get_model_provider_status(provider_id: str | None = None) -> dict[str, Any]:
|
|
|
182
245
|
kwargs = {"api_key": key}
|
|
183
246
|
if provider_id in OPENAI_COMPATIBLE_BASE_URLS:
|
|
184
247
|
kwargs["base_url"] = OPENAI_COMPATIBLE_BASE_URLS[provider_id]
|
|
185
|
-
mod
|
|
248
|
+
_construct_openai_client(mod, kwargs)
|
|
186
249
|
status["client_constructed_ok"] = True
|
|
187
250
|
status["provider_available"] = True
|
|
188
251
|
except Exception as exc:
|
|
@@ -232,7 +295,7 @@ def _openai_compatible_response(provider_id: str, model_name: str, prompt: str)
|
|
|
232
295
|
settings = get_settings()
|
|
233
296
|
_env_var, key = _provider_key(provider_id, settings)
|
|
234
297
|
mod = import_openai_sdk()
|
|
235
|
-
kwargs = {"api_key": key}
|
|
298
|
+
kwargs = {"api_key": key, "timeout": _provider_request_timeout_seconds()}
|
|
236
299
|
if provider_id in OPENAI_COMPATIBLE_BASE_URLS:
|
|
237
300
|
kwargs["base_url"] = OPENAI_COMPATIBLE_BASE_URLS[provider_id]
|
|
238
301
|
if provider_id == "openrouter":
|
|
@@ -243,7 +306,7 @@ def _openai_compatible_response(provider_id: str, model_name: str, prompt: str)
|
|
|
243
306
|
}
|
|
244
307
|
except Exception:
|
|
245
308
|
pass
|
|
246
|
-
client = mod
|
|
309
|
+
client = _construct_openai_client(mod, kwargs)
|
|
247
310
|
if provider_id == "openai":
|
|
248
311
|
return _response_text(_responses_create_with_retry(client, model_name, prompt))
|
|
249
312
|
response = client.chat.completions.create(
|
|
@@ -267,7 +330,7 @@ def _anthropic_response(model_name: str, prompt: str) -> str:
|
|
|
267
330
|
"https://api.anthropic.com/v1/messages",
|
|
268
331
|
{"x-api-key": key or "", "anthropic-version": "2023-06-01"},
|
|
269
332
|
payload,
|
|
270
|
-
timeout=
|
|
333
|
+
timeout=_provider_request_timeout_seconds(),
|
|
271
334
|
)
|
|
272
335
|
parts = data.get("content") or []
|
|
273
336
|
text = "".join(str(part.get("text") or "") for part in parts if isinstance(part, dict))
|
|
@@ -284,7 +347,7 @@ def _google_response(model_name: str, prompt: str) -> str:
|
|
|
284
347
|
"contents": [{"parts": [{"text": prompt}]}],
|
|
285
348
|
"generationConfig": {"responseMimeType": "application/json", "maxOutputTokens": _provider_max_output_tokens()},
|
|
286
349
|
}
|
|
287
|
-
data = _http_json(url, {}, payload, timeout=
|
|
350
|
+
data = _http_json(url, {}, payload, timeout=_provider_request_timeout_seconds())
|
|
288
351
|
try:
|
|
289
352
|
return str(data["candidates"][0]["content"]["parts"][0]["text"])
|
|
290
353
|
except Exception as exc:
|
|
@@ -301,13 +364,14 @@ def _provider_completion_text(provider_id: str, model_name: str, prompt: str) ->
|
|
|
301
364
|
|
|
302
365
|
def _provider_completion_text_with_retry(provider_id: str, model_name: str, prompt: str) -> str:
|
|
303
366
|
last: BaseException | None = None
|
|
304
|
-
|
|
367
|
+
attempts = _provider_request_retry_count()
|
|
368
|
+
for i in range(attempts):
|
|
305
369
|
try:
|
|
306
370
|
return _provider_completion_text(provider_id, model_name, prompt)
|
|
307
371
|
except Exception as exc:
|
|
308
372
|
last = exc
|
|
309
373
|
kind, _msg = classify_model_provider_error(exc)
|
|
310
|
-
if i
|
|
374
|
+
if i >= attempts - 1 or kind in {"auth_error", "quota_or_credit_error", "bad_model"}:
|
|
311
375
|
raise
|
|
312
376
|
time.sleep(0.75)
|
|
313
377
|
raise last or RuntimeError("Mentor Model Provider request failed.")
|
|
@@ -402,7 +466,7 @@ def _transient_provider_error(exc: BaseException) -> bool:
|
|
|
402
466
|
needles=['timeout','rate','temporar','connection reset','disconnect','websocket','service unavailable','gateway','overloaded']
|
|
403
467
|
return any(n in name or n in msg for n in needles)
|
|
404
468
|
|
|
405
|
-
def _responses_create_with_retry(client: Any, model_name: str, prompt: str, attempts: int =
|
|
469
|
+
def _responses_create_with_retry(client: Any, model_name: str, prompt: str, attempts: int = 1):
|
|
406
470
|
last=None
|
|
407
471
|
for i in range(attempts):
|
|
408
472
|
try:
|
|
@@ -453,7 +517,10 @@ def run_structured_role(role: str, prompt: str, output_model: type[BaseModel], o
|
|
|
453
517
|
raise RuntimeError(result.error_message)
|
|
454
518
|
return result
|
|
455
519
|
try:
|
|
456
|
-
raw=
|
|
520
|
+
raw=_with_wall_timeout(
|
|
521
|
+
f"{status.get('provider_display') or provider_id} {role}",
|
|
522
|
+
lambda: _provider_completion_text_with_retry(provider_id, model_name, prompt),
|
|
523
|
+
)
|
|
457
524
|
(out_dir/'raw_output.txt').write_text(redact_secrets(raw))
|
|
458
525
|
try:
|
|
459
526
|
normalized,json_meta,normalized_used=_validate_role_output(role, raw, output_model, normalizer_context, out_dir)
|
|
@@ -464,7 +531,10 @@ def run_structured_role(role: str, prompt: str, output_model: type[BaseModel], o
|
|
|
464
531
|
retry_raw=None
|
|
465
532
|
try:
|
|
466
533
|
retry_prompt=_strict_json_retry_prompt(role, prompt, first_error)
|
|
467
|
-
retry_raw=
|
|
534
|
+
retry_raw=_with_wall_timeout(
|
|
535
|
+
f"{status.get('provider_display') or provider_id} {role} structured retry",
|
|
536
|
+
lambda: _provider_completion_text_with_retry(provider_id, model_name, retry_prompt),
|
|
537
|
+
)
|
|
468
538
|
(out_dir/'raw_output.retry.txt').write_text(redact_secrets(retry_raw))
|
|
469
539
|
normalized,json_meta,normalized_used=_validate_role_output(role, retry_raw, output_model, normalizer_context, out_dir)
|
|
470
540
|
write_json(out_dir/'parsed_output.json', normalized)
|
|
@@ -520,7 +590,7 @@ def smoke_prompts() -> dict[str, tuple[str, type[BaseModel]]]:
|
|
|
520
590
|
"pass_threshold": 0.7,
|
|
521
591
|
"observable_evidence": ["artifacts/hello.txt"],
|
|
522
592
|
"required_artifacts": ["hello.txt"],
|
|
523
|
-
"scoring_method": "
|
|
593
|
+
"scoring_method": "hybrid",
|
|
524
594
|
"worker_visible": True,
|
|
525
595
|
"verifier_only": False,
|
|
526
596
|
"hidden_reference_required": False,
|
|
@@ -613,8 +683,8 @@ def smoke_prompts() -> dict[str, tuple[str, type[BaseModel]]]:
|
|
|
613
683
|
"required_artifacts": ["hello.txt"],
|
|
614
684
|
"disqualifying_errors": [],
|
|
615
685
|
"partial_credit_allowed": True,
|
|
616
|
-
"grader_kind": "
|
|
617
|
-
"rubric_generation_source": "
|
|
686
|
+
"grader_kind": "hybrid",
|
|
687
|
+
"rubric_generation_source": "agent_assisted",
|
|
618
688
|
"rubric_generation_agent_provider": None,
|
|
619
689
|
"rubric_generation_agent_model": None,
|
|
620
690
|
"rubric_generation_confidence": 0.9,
|
|
@@ -7,7 +7,7 @@ from .rubric_generation import worker_visible_markdown
|
|
|
7
7
|
from .artifact_resolver import artifact_ref_candidates, normalize_artifact_ref
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
SOURCE_FIELD_KEYS={'source_url_or_ref','source_kind','source_url','source_ref','source_license'}
|
|
10
|
+
SOURCE_FIELD_KEYS={'source_url_or_ref','source_kind','source_url','source_ref','source_basis','source_license'}
|
|
11
11
|
IGNORED_RELEASE_DIR_NAMES={
|
|
12
12
|
'node_modules','.venv','__pycache__','.pytest_cache','.mypy_cache','.ruff_cache','.git','.cache','dist','build'
|
|
13
13
|
}
|
|
@@ -52,6 +52,7 @@ def _base_status(run_id: str, run_root: Path, **kwargs: Any) -> dict[str, Any]:
|
|
|
52
52
|
"maximum_improvement_loops": kwargs.get("maximum_improvement_loops"),
|
|
53
53
|
"latest_message": kwargs.get("latest_message") or "Run starting.",
|
|
54
54
|
"apprentice_agent": kwargs.get("apprentice_agent") or kwargs.get("worker_agent"),
|
|
55
|
+
"apprenticeship_mode": kwargs.get("apprenticeship_mode"),
|
|
55
56
|
"mentor_mode": kwargs.get("mentor_mode"),
|
|
56
57
|
"task_title": kwargs.get("task_title"),
|
|
57
58
|
"task_workspace_path": str(kwargs.get("task_workspace_path") or run_root),
|
|
@@ -191,7 +192,7 @@ def format_run_status(status: dict[str, Any]) -> str:
|
|
|
191
192
|
f"Artifacts: {status.get('artifacts_path')}",
|
|
192
193
|
]
|
|
193
194
|
if status.get("contribution_bundle_path"):
|
|
194
|
-
lines.append(f"
|
|
195
|
+
lines.append(f"Experience Compilation: {status.get('contribution_bundle_path')}")
|
|
195
196
|
if status.get("traced_steps") is not None:
|
|
196
197
|
lines.append(f"Traced Steps: {status.get('traced_steps')}")
|
|
197
198
|
if status.get("artifact_count") is not None:
|