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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nodloop
3
- Version: 0.10.0
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodloop"
3
- version = "0.10.0"
3
+ version = "0.10.1"
4
4
  description = "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
  readme = "README.md"
6
6
  license = "MIT"
@@ -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.0"
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(self, run: str, result: Result, confirmed_cause: str = "", note: str = "", reviewer: str = "") -> None:
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: dict[str, Any]
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[1]
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[1]
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"
@@ -900,7 +900,7 @@ wheels = [
900
900
 
901
901
  [[package]]
902
902
  name = "nodloop"
903
- version = "0.10.0"
903
+ version = "0.10.1"
904
904
  source = { editable = "." }
905
905
  dependencies = [
906
906
  { name = "mcp" },
File without changes
File without changes
File without changes
File without changes