nodloop 0.10.0__tar.gz → 0.10.1__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.
- {nodloop-0.10.0 → nodloop-0.10.1}/PKG-INFO +1 -1
- {nodloop-0.10.0 → nodloop-0.10.1}/pyproject.toml +1 -1
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/client.py +14 -3
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/records.py +61 -6
- {nodloop-0.10.0 → nodloop-0.10.1}/tests/test_client.py +6 -1
- {nodloop-0.10.0 → nodloop-0.10.1}/uv.lock +1 -1
- {nodloop-0.10.0 → nodloop-0.10.1}/.gitignore +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/README.md +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/__init__.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/binary.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/claude_agent.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/langchain.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/src/nodloop/openai_agents.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/tests/conftest.py +0 -0
- {nodloop-0.10.0 → nodloop-0.10.1}/tests/test_adapters.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nodloop
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Record AI runs and the verdicts people give on them, and hand the knowledge they approve to the next run in the same place
|
|
5
5
|
Project-URL: Homepage, https://github.com/jeon-jihyeon/nodloop
|
|
6
6
|
Project-URL: Repository, https://github.com/jeon-jihyeon/nodloop
|
|
@@ -33,7 +33,7 @@ __all__ = [
|
|
|
33
33
|
]
|
|
34
34
|
|
|
35
35
|
# The binary release the client fetches when no nodloop is installed
|
|
36
|
-
VERSION = "0.10.
|
|
36
|
+
VERSION = "0.10.1"
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
# The reports report reads
|
|
@@ -330,15 +330,26 @@ class Client:
|
|
|
330
330
|
args["edited_output"] = edited
|
|
331
331
|
await self.call("feedback", args)
|
|
332
332
|
|
|
333
|
-
async def outcome(
|
|
333
|
+
async def outcome(
|
|
334
|
+
self,
|
|
335
|
+
run: str,
|
|
336
|
+
result: Result,
|
|
337
|
+
confirmed_cause: str = "",
|
|
338
|
+
note: str = "",
|
|
339
|
+
reviewer: str = "",
|
|
340
|
+
*,
|
|
341
|
+
actor: Literal["person", "code", "model", "unknown"] | None = None,
|
|
342
|
+
) -> None:
|
|
334
343
|
"""Records what a real check found for a run: confirmed, refuted or inconclusive
|
|
335
344
|
|
|
336
345
|
A different fact from the verdict on the output
|
|
337
346
|
confirmed_cause only goes with confirmed
|
|
338
347
|
reviewer names who checked and is required over stdio while a server key names itself
|
|
348
|
+
actor names what kind of evaluator checked
|
|
349
|
+
It never proves a person since only the transport does
|
|
339
350
|
"""
|
|
340
351
|
args: dict[str, Any] = {"trace_id": run, "result": result}
|
|
341
|
-
for key, value in (("confirmed_cause", confirmed_cause), ("note", note), ("reviewer", reviewer)):
|
|
352
|
+
for key, value in (("confirmed_cause", confirmed_cause), ("note", note), ("reviewer", reviewer), ("actor", actor)):
|
|
342
353
|
if value:
|
|
343
354
|
args[key] = value
|
|
344
355
|
await self.call("outcome", args)
|
|
@@ -244,6 +244,7 @@ class _ExtractRecordOutputAttemptsDraftRequired(TypedDict):
|
|
|
244
244
|
class ExtractRecordOutputAttemptsDraft(_ExtractRecordOutputAttemptsDraftRequired, total=False):
|
|
245
245
|
relates_to: str
|
|
246
246
|
keys: list[str] | None
|
|
247
|
+
excerpt: str
|
|
247
248
|
|
|
248
249
|
|
|
249
250
|
class ExtractRecordOutputAttemptsCritique(TypedDict):
|
|
@@ -273,6 +274,22 @@ class ExtractRecordOutputRelated(TypedDict):
|
|
|
273
274
|
status: Literal["candidate", "approved", "retired", "superseded"]
|
|
274
275
|
|
|
275
276
|
|
|
277
|
+
class _ExtractRecordOutputTriageRequired(TypedDict):
|
|
278
|
+
policy: str
|
|
279
|
+
nature: Literal["preference", "safety", "organization", "unknown"]
|
|
280
|
+
mixed: bool
|
|
281
|
+
reversibility: Literal["H", "M", "L", "unknown"]
|
|
282
|
+
source: Literal["complete", "incomplete", "missing", "scope"]
|
|
283
|
+
protected: Literal["none", "safety", "organization", "mixed", "conflict", "artifact", "unknown"]
|
|
284
|
+
lasting: bool
|
|
285
|
+
faithful: bool
|
|
286
|
+
conflict: bool
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class ExtractRecordOutputTriage(_ExtractRecordOutputTriageRequired, total=False):
|
|
290
|
+
classify: str
|
|
291
|
+
|
|
292
|
+
|
|
276
293
|
class _ExtractRecordOutputRequired(TypedDict):
|
|
277
294
|
attempts: list[ExtractRecordOutputAttempts] | None
|
|
278
295
|
conclusion: str
|
|
@@ -281,6 +298,8 @@ class _ExtractRecordOutputRequired(TypedDict):
|
|
|
281
298
|
class ExtractRecordOutput(_ExtractRecordOutputRequired, total=False):
|
|
282
299
|
candidate: ExtractRecordOutputCandidate | None
|
|
283
300
|
related: ExtractRecordOutputRelated | None
|
|
301
|
+
excerpt: str
|
|
302
|
+
triage: ExtractRecordOutputTriage | None
|
|
284
303
|
|
|
285
304
|
|
|
286
305
|
class _ExtractRecordUsageRequired(TypedDict):
|
|
@@ -526,6 +545,15 @@ class RefillRecord(_RefillRecordRequired, total=False):
|
|
|
526
545
|
labels: dict[str, list[str] | None]
|
|
527
546
|
|
|
528
547
|
|
|
548
|
+
class TurnRecordInputContext(TypedDict):
|
|
549
|
+
id: str
|
|
550
|
+
version: int
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class TurnRecordInput(TypedDict, total=False):
|
|
554
|
+
context: list[TurnRecordInputContext] | None
|
|
555
|
+
|
|
556
|
+
|
|
529
557
|
class _TurnRecordUsageRequired(TypedDict):
|
|
530
558
|
input_tokens: int
|
|
531
559
|
output_tokens: int
|
|
@@ -544,14 +572,14 @@ class _TurnRecordRequired(TypedDict):
|
|
|
544
572
|
id: str
|
|
545
573
|
name: Literal["turn"]
|
|
546
574
|
time: str
|
|
547
|
-
input:
|
|
575
|
+
input: TurnRecordInput
|
|
548
576
|
usage: TurnRecordUsage
|
|
549
577
|
duration_ms: int
|
|
550
578
|
schema_version: Literal[1]
|
|
551
579
|
|
|
552
580
|
|
|
553
581
|
class TurnRecord(_TurnRecordRequired, total=False):
|
|
554
|
-
"""The start of a turn of a session"""
|
|
582
|
+
"""The start of a turn of a session with its context item references"""
|
|
555
583
|
|
|
556
584
|
session_id: str
|
|
557
585
|
subject: str
|
|
@@ -741,9 +769,26 @@ class DecideRecordInput(_DecideRecordInputRequired, total=False):
|
|
|
741
769
|
proposal: str
|
|
742
770
|
|
|
743
771
|
|
|
772
|
+
class _DecideRecordOutputTriageRequired(TypedDict):
|
|
773
|
+
policy: str
|
|
774
|
+
nature: Literal["preference", "safety", "organization", "unknown"]
|
|
775
|
+
mixed: bool
|
|
776
|
+
reversibility: Literal["H", "M", "L", "unknown"]
|
|
777
|
+
source: Literal["complete", "incomplete", "missing", "scope"]
|
|
778
|
+
protected: Literal["none", "safety", "organization", "mixed", "conflict", "artifact", "unknown"]
|
|
779
|
+
lasting: bool
|
|
780
|
+
faithful: bool
|
|
781
|
+
conflict: bool
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
class DecideRecordOutputTriage(_DecideRecordOutputTriageRequired, total=False):
|
|
785
|
+
classify: str
|
|
786
|
+
grant: str
|
|
787
|
+
|
|
788
|
+
|
|
744
789
|
class _DecideRecordOutputRequired(TypedDict):
|
|
745
790
|
tier: int
|
|
746
|
-
action: Literal["apply", "withdraw", "drop", "join", "ask", "shadow", "retry"]
|
|
791
|
+
action: Literal["apply", "withdraw", "drop", "join", "ask", "shadow", "retry", "hold"]
|
|
747
792
|
|
|
748
793
|
|
|
749
794
|
class DecideRecordOutput(_DecideRecordOutputRequired, total=False):
|
|
@@ -763,6 +808,8 @@ class DecideRecordOutput(_DecideRecordOutputRequired, total=False):
|
|
|
763
808
|
"judge_against",
|
|
764
809
|
"judge_undecided",
|
|
765
810
|
"judge_split",
|
|
811
|
+
"delegated",
|
|
812
|
+
"triage_protected",
|
|
766
813
|
]
|
|
767
814
|
judge: str
|
|
768
815
|
outcome: Literal["conform", "against", "undecided", "split"]
|
|
@@ -772,6 +819,8 @@ class DecideRecordOutput(_DecideRecordOutputRequired, total=False):
|
|
|
772
819
|
reason: Literal["duplicate", "no_evidence", "temp_scope", "conflict", "words", "compaction", "person"]
|
|
773
820
|
failure: Literal["hard", "repairable", "underexplored", "unpromising"]
|
|
774
821
|
reopen: Literal["other_session", "permanent_place", "next_pass", "person_only"]
|
|
822
|
+
triage: DecideRecordOutputTriage | None
|
|
823
|
+
delegation: str
|
|
775
824
|
|
|
776
825
|
|
|
777
826
|
class _DecideRecordUsageRequired(TypedDict):
|
|
@@ -808,7 +857,7 @@ class _DecideRecordRequired(TypedDict):
|
|
|
808
857
|
input: DecideRecordInput
|
|
809
858
|
usage: DecideRecordUsage
|
|
810
859
|
duration_ms: int
|
|
811
|
-
schema_version: Literal[
|
|
860
|
+
schema_version: Literal[2]
|
|
812
861
|
|
|
813
862
|
|
|
814
863
|
class DecideRecord(_DecideRecordRequired, total=False):
|
|
@@ -862,6 +911,7 @@ class KnobRecordInputKnobs(_KnobRecordInputKnobsRequired, total=False):
|
|
|
862
911
|
audit: KnobRecordInputKnobsAudit
|
|
863
912
|
target: KnobRecordInputKnobsTarget
|
|
864
913
|
cusum: KnobRecordInputKnobsCusum
|
|
914
|
+
delegation_paused: list[str] | None
|
|
865
915
|
|
|
866
916
|
|
|
867
917
|
class KnobRecordInputJudgeSets(TypedDict):
|
|
@@ -878,8 +928,10 @@ class _KnobRecordInputJudgeRequired(TypedDict):
|
|
|
878
928
|
|
|
879
929
|
|
|
880
930
|
class KnobRecordInputJudge(_KnobRecordInputJudgeRequired, total=False):
|
|
931
|
+
backend: str
|
|
881
932
|
kept: int
|
|
882
933
|
refused: int
|
|
934
|
+
answered: str
|
|
883
935
|
|
|
884
936
|
|
|
885
937
|
class _KnobRecordInputChangesOpeningRequired(TypedDict):
|
|
@@ -891,7 +943,7 @@ class KnobRecordInputChangesOpening(_KnobRecordInputChangesOpeningRequired, tota
|
|
|
891
943
|
|
|
892
944
|
|
|
893
945
|
class _KnobRecordInputChangesRequired(TypedDict):
|
|
894
|
-
rule: Literal["posterior", "alarm", "raise", "lower", "person"]
|
|
946
|
+
rule: Literal["posterior", "alarm", "raise", "lower", "person", "delegation"]
|
|
895
947
|
|
|
896
948
|
|
|
897
949
|
class KnobRecordInputChanges(_KnobRecordInputChangesRequired, total=False):
|
|
@@ -900,6 +952,7 @@ class KnobRecordInputChanges(_KnobRecordInputChangesRequired, total=False):
|
|
|
900
952
|
differ: int
|
|
901
953
|
p: float
|
|
902
954
|
knob: str
|
|
955
|
+
grant: str
|
|
903
956
|
|
|
904
957
|
|
|
905
958
|
class _KnobRecordInputRequired(TypedDict):
|
|
@@ -938,7 +991,7 @@ class _KnobRecordRequired(TypedDict):
|
|
|
938
991
|
input: KnobRecordInput
|
|
939
992
|
usage: KnobRecordUsage
|
|
940
993
|
duration_ms: int
|
|
941
|
-
schema_version: Literal[
|
|
994
|
+
schema_version: Literal[2]
|
|
942
995
|
|
|
943
996
|
|
|
944
997
|
class KnobRecord(_KnobRecordRequired, total=False):
|
|
@@ -1384,6 +1437,8 @@ class OutcomeRecord(_OutcomeRecordRequired, total=False):
|
|
|
1384
1437
|
|
|
1385
1438
|
confirmed_cause: str
|
|
1386
1439
|
note: str
|
|
1440
|
+
actor: Literal["person", "code", "model", "unknown"]
|
|
1441
|
+
proof: Literal["key", "terminal", "elicitation", "reply", "none"]
|
|
1387
1442
|
|
|
1388
1443
|
|
|
1389
1444
|
class _KnowledgeRecordRunRequired(TypedDict):
|
|
@@ -304,13 +304,18 @@ async def test_outcome_and_report(binary: str, records: str) -> None:
|
|
|
304
304
|
(Path(records) / "prune.json").write_text('{"at":"2020-01-02T00:00:00Z","cutoff":"2020-01-01T00:00:00Z"}')
|
|
305
305
|
async with Client(records, binary) as c:
|
|
306
306
|
run = await c.record("stream-bot", "lag comes from partition 7", labels=ACME)
|
|
307
|
-
await c.outcome(run, "confirmed", confirmed_cause="partition 7 was hot", note="checked the broker", reviewer="ann")
|
|
307
|
+
await c.outcome(run, "confirmed", confirmed_cause="partition 7 was hot", note="checked the broker", reviewer="ann", actor="code")
|
|
308
|
+
outcomes = await c.records("outcome")
|
|
309
|
+
assert outcomes.records[0]["actor"] == "code"
|
|
310
|
+
assert "proof" not in outcomes.records[0]
|
|
308
311
|
health = await c.report("health")
|
|
309
312
|
loop = await c.report("loop")
|
|
310
313
|
with pytest.raises(ToolError):
|
|
311
314
|
await c.outcome(run, "refuted", confirmed_cause="partition 7 was hot", reviewer="ann")
|
|
312
315
|
with pytest.raises(ToolError, match="a reviewer is required"):
|
|
313
316
|
await c.outcome(run, "refuted")
|
|
317
|
+
with pytest.raises(ToolError, match="actor"):
|
|
318
|
+
await c.call("outcome", {"trace_id": run, "result": "confirmed", "reviewer": "ann", "actor": "future"})
|
|
314
319
|
|
|
315
320
|
assert isinstance(health.report, (list, dict)) and loop.report["totals"]["runs"] == 1
|
|
316
321
|
assert loop.since == "2020-01-01T00:00:00Z"
|
|
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
|