cc-transcript 3.2.0__tar.gz → 3.3.0__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.
Files changed (56) hide show
  1. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/PKG-INFO +1 -1
  2. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/__init__.py +8 -0
  3. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/models.py +33 -1
  4. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/parser.py +5 -1
  5. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/render.py +6 -0
  6. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/pyproject.toml +1 -1
  7. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/event.rs +11 -7
  8. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/model.rs +2 -0
  9. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/Cargo.lock +0 -0
  10. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/Cargo.toml +0 -0
  11. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/LICENSE +0 -0
  12. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/README.md +0 -0
  13. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/__main__.py +0 -0
  14. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/_parser_rs.pyi +0 -0
  15. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/activity.py +0 -0
  16. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/backend.py +0 -0
  17. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/builders.py +0 -0
  18. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/cli.py +0 -0
  19. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/context.py +0 -0
  20. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/corrections.py +0 -0
  21. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/decisions.py +0 -0
  22. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/discovery.py +0 -0
  23. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/disktruth.py +0 -0
  24. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/evidence.py +0 -0
  25. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/filterspec.py +0 -0
  26. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/ids.py +0 -0
  27. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/judge/__init__.py +0 -0
  28. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/judge/llm.py +0 -0
  29. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/judge/verdicts.py +0 -0
  30. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/messages.py +0 -0
  31. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/__init__.py +0 -0
  32. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/candidates.py +0 -0
  33. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/confidence.py +0 -0
  34. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/filterspec.py +0 -0
  35. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/formats.py +0 -0
  36. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/signals.py +0 -0
  37. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/sourcekind.py +0 -0
  38. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/mining/store.py +0 -0
  39. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/py.typed +0 -0
  40. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/query.py +0 -0
  41. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/rust.py +0 -0
  42. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/sentiment/__init__.py +0 -0
  43. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/sentiment/buckets.py +0 -0
  44. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/sentiment/engine.py +0 -0
  45. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/sentiment/lexicon.py +0 -0
  46. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/sentiment/scorespec.py +0 -0
  47. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/store.py +0 -0
  48. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/cc_transcript/tools.py +0 -0
  49. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/Cargo.toml +0 -0
  50. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/data/afinn-en-165.tsv +0 -0
  51. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/data/domain_overrides.tsv +0 -0
  52. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/filter.rs +0 -0
  53. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/lexicon.rs +0 -0
  54. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/lib.rs +0 -0
  55. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/score.rs +0 -0
  56. {cc_transcript-3.2.0 → cc_transcript-3.3.0}/rust/src/value.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-transcript
3
- Version: 3.2.0
3
+ Version: 3.3.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -60,7 +60,9 @@ EXPORTS: dict[str, str] = {
60
60
  "CcVersion",
61
61
  "ContentBlock",
62
62
  "EntryMeta",
63
+ "FallbackBlock",
63
64
  "ModeEvent",
65
+ "OtherBlock",
64
66
  "OtherEvent",
65
67
  "SystemEvent",
66
68
  "TextBlock",
@@ -426,9 +428,15 @@ if TYPE_CHECKING:
426
428
  from cc_transcript.models import (
427
429
  EntryMeta as EntryMeta,
428
430
  )
431
+ from cc_transcript.models import (
432
+ FallbackBlock as FallbackBlock,
433
+ )
429
434
  from cc_transcript.models import (
430
435
  ModeEvent as ModeEvent,
431
436
  )
437
+ from cc_transcript.models import (
438
+ OtherBlock as OtherBlock,
439
+ )
432
440
  from cc_transcript.models import (
433
441
  OtherEvent as OtherEvent,
434
442
  )
@@ -83,7 +83,39 @@ class ToolResultBlock:
83
83
  is_async: bool = False
84
84
 
85
85
 
86
- ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
86
+ @dataclass(frozen=True, slots=True)
87
+ class FallbackBlock:
88
+ """A marker that the assistant turn fell back from one model to another.
89
+
90
+ Claude Code records this when a turn switches models mid-stream; it carries
91
+ no message content, only the two model names.
92
+
93
+ Attributes:
94
+ from_model: The model the turn started on.
95
+ to_model: The model the turn fell back to.
96
+ """
97
+
98
+ from_model: str
99
+ to_model: str
100
+
101
+
102
+ @dataclass(frozen=True, slots=True)
103
+ class OtherBlock:
104
+ """Any assistant content block whose ``type`` is not yet modeled.
105
+
106
+ The escape hatch that keeps an unrecognized block from crashing the parser
107
+ as Claude Code's transcript format evolves, mirroring :class:`OtherEvent`.
108
+
109
+ Attributes:
110
+ type: The block's ``type`` field.
111
+ raw: The block's full decoded payload.
112
+ """
113
+
114
+ type: str
115
+ raw: Mapping[str, Any]
116
+
117
+
118
+ ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock | FallbackBlock | OtherBlock
87
119
 
88
120
 
89
121
  @dataclass(frozen=True, slots=True)
@@ -18,7 +18,9 @@ from cc_transcript.models import (
18
18
  ContentBlock,
19
19
  EntryMeta,
20
20
  EventUuid,
21
+ FallbackBlock,
21
22
  ModeEvent,
23
+ OtherBlock,
22
24
  OtherEvent,
23
25
  SessionId,
24
26
  SystemEvent,
@@ -102,8 +104,10 @@ def parse_assistant_block(block: dict[str, Any]) -> ContentBlock:
102
104
  return ThinkingBlock(block["thinking"])
103
105
  case "tool_use":
104
106
  return ToolUseBlock(id=ToolUseId(block["id"]), name=block["name"], input=block["input"])
107
+ case "fallback":
108
+ return FallbackBlock(from_model=block["from"]["model"], to_model=block["to"]["model"])
105
109
  case unknown:
106
- raise ValueError(f"unexpected assistant block type: {unknown}")
110
+ return OtherBlock(type=unknown, raw=block)
107
111
 
108
112
 
109
113
  def parse_event(data: Mapping[str, Any]) -> TranscriptEvent | None:
@@ -13,7 +13,9 @@ import orjson
13
13
  from cc_transcript.filterspec import event_kind, event_meta, event_text, tool_uses
14
14
  from cc_transcript.models import (
15
15
  AssistantEvent,
16
+ FallbackBlock,
16
17
  ModeEvent,
18
+ OtherBlock,
17
19
  OtherEvent,
18
20
  SystemEvent,
19
21
  TextBlock,
@@ -230,6 +232,10 @@ def block_payload(block: ContentBlock, *, width: int, thinking: bool) -> str:
230
232
  )
231
233
  case ToolResultBlock():
232
234
  return ""
235
+ case FallbackBlock(from_model=src, to_model=dst):
236
+ return f"fallback {src}->{dst}"
237
+ case OtherBlock(type=type_):
238
+ return type_
233
239
 
234
240
 
235
241
  def line_budget(width: int) -> Budget:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-transcript"
3
- version = "3.2.0"
3
+ version = "3.3.0"
4
4
  description = "The Claude Code session-activity platform: a superset JSONL parser (Rust fast path, Python reference), typed tool calls with a cross-language content digest, turns/edits/evidence over one spine, context windows with labeled fidelity, unified decision and correction ledgers, LLM judging, and a transcript-investigation CLI."
5
5
  readme = "README.md"
6
6
  license = "PolyForm-Noncommercial-1.0.0"
@@ -6,9 +6,9 @@ use pyo3::IntoPyObjectExt;
6
6
  use sonic_rs::{JsonContainerTrait, JsonType, JsonValueTrait, Value};
7
7
 
8
8
  use crate::model::{
9
- models_type, ASSISTANT_EVENT_CLS, ENTRY_META_CLS, MODE_EVENT_CLS, OTHER_EVENT_CLS,
10
- SYSTEM_EVENT_CLS, TEXT_BLOCK_CLS, THINKING_BLOCK_CLS, TOOL_RESULT_BLOCK_CLS,
11
- TOOL_USE_BLOCK_CLS, USER_EVENT_CLS,
9
+ models_type, ASSISTANT_EVENT_CLS, ENTRY_META_CLS, FALLBACK_BLOCK_CLS, MODE_EVENT_CLS,
10
+ OTHER_BLOCK_CLS, OTHER_EVENT_CLS, SYSTEM_EVENT_CLS, TEXT_BLOCK_CLS, THINKING_BLOCK_CLS,
11
+ TOOL_RESULT_BLOCK_CLS, TOOL_USE_BLOCK_CLS, USER_EVENT_CLS,
12
12
  };
13
13
  use crate::value::{block_type, field, field_bool, field_str};
14
14
 
@@ -148,10 +148,14 @@ fn parse_assistant_block<'py>(py: Python<'py>, block: &Value) -> PyResult<Bound<
148
148
  require_str(block, "name")?,
149
149
  json_to_py(py, require(block, "input")?)?,
150
150
  )),
151
- other => Err(PyValueError::new_err(format!(
152
- "unexpected assistant block type: {}",
153
- other.unwrap_or("None")
154
- ))),
151
+ Some("fallback") => models_type(py, &FALLBACK_BLOCK_CLS, "FallbackBlock")?.call1((
152
+ require_str(require(block, "from")?, "model")?,
153
+ require_str(require(block, "to")?, "model")?,
154
+ )),
155
+ Some(other) => {
156
+ models_type(py, &OTHER_BLOCK_CLS, "OtherBlock")?.call1((other, json_to_py(py, block)?))
157
+ }
158
+ None => Err(PyKeyError::new_err("'type'")),
155
159
  }
156
160
  }
157
161
 
@@ -12,6 +12,8 @@ pub static TEXT_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
12
12
  pub static THINKING_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
13
13
  pub static TOOL_USE_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
14
14
  pub static TOOL_RESULT_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
15
+ pub static FALLBACK_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
16
+ pub static OTHER_BLOCK_CLS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
15
17
 
16
18
  pub fn models_type<'py>(
17
19
  py: Python<'py>,
File without changes
File without changes
File without changes
File without changes