docent-python 0.1.14a0__py3-none-any.whl → 0.1.28a0__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.

Potentially problematic release.


This version of docent-python might be problematic. Click here for more details.

Files changed (46) hide show
  1. docent/_llm_util/__init__.py +0 -0
  2. docent/_llm_util/data_models/__init__.py +0 -0
  3. docent/_llm_util/data_models/exceptions.py +48 -0
  4. docent/_llm_util/data_models/llm_output.py +331 -0
  5. docent/_llm_util/llm_cache.py +193 -0
  6. docent/_llm_util/llm_svc.py +472 -0
  7. docent/_llm_util/model_registry.py +130 -0
  8. docent/_llm_util/providers/__init__.py +0 -0
  9. docent/_llm_util/providers/anthropic.py +537 -0
  10. docent/_llm_util/providers/common.py +41 -0
  11. docent/_llm_util/providers/google.py +530 -0
  12. docent/_llm_util/providers/openai.py +745 -0
  13. docent/_llm_util/providers/openrouter.py +375 -0
  14. docent/_llm_util/providers/preference_types.py +104 -0
  15. docent/_llm_util/providers/provider_registry.py +164 -0
  16. docent/data_models/__init__.py +2 -0
  17. docent/data_models/agent_run.py +17 -29
  18. docent/data_models/chat/__init__.py +6 -1
  19. docent/data_models/chat/message.py +3 -1
  20. docent/data_models/citation.py +103 -22
  21. docent/data_models/judge.py +19 -0
  22. docent/data_models/metadata_util.py +16 -0
  23. docent/data_models/remove_invalid_citation_ranges.py +23 -10
  24. docent/data_models/transcript.py +25 -80
  25. docent/data_models/util.py +170 -0
  26. docent/judges/__init__.py +23 -0
  27. docent/judges/analysis.py +77 -0
  28. docent/judges/impl.py +587 -0
  29. docent/judges/runner.py +129 -0
  30. docent/judges/stats.py +205 -0
  31. docent/judges/types.py +311 -0
  32. docent/judges/util/forgiving_json.py +108 -0
  33. docent/judges/util/meta_schema.json +86 -0
  34. docent/judges/util/meta_schema.py +29 -0
  35. docent/judges/util/parse_output.py +87 -0
  36. docent/judges/util/voting.py +139 -0
  37. docent/sdk/agent_run_writer.py +72 -21
  38. docent/sdk/client.py +276 -23
  39. docent/trace.py +413 -90
  40. {docent_python-0.1.14a0.dist-info → docent_python-0.1.28a0.dist-info}/METADATA +13 -5
  41. docent_python-0.1.28a0.dist-info/RECORD +59 -0
  42. docent/data_models/metadata.py +0 -229
  43. docent/data_models/yaml_util.py +0 -12
  44. docent_python-0.1.14a0.dist-info/RECORD +0 -32
  45. {docent_python-0.1.14a0.dist-info → docent_python-0.1.28a0.dist-info}/WHEEL +0 -0
  46. {docent_python-0.1.14a0.dist-info → docent_python-0.1.28a0.dist-info}/licenses/LICENSE.md +0 -0
@@ -17,8 +17,8 @@ from pydantic_core import to_jsonable_python
17
17
 
18
18
  from docent._log_util import get_logger
19
19
  from docent.data_models._tiktoken_util import get_token_count, group_messages_into_ranges
20
+ from docent.data_models.metadata_util import dump_metadata
20
21
  from docent.data_models.transcript import Transcript, TranscriptGroup
21
- from docent.data_models.yaml_util import yaml_dump_metadata
22
22
 
23
23
  logger = get_logger(__name__)
24
24
 
@@ -125,6 +125,7 @@ class AgentRun(BaseModel):
125
125
  # )
126
126
 
127
127
  # Append the text field
128
+ result.append({"name": "agent_run_id", "type": "str"})
128
129
  result.append({"name": "text", "type": "str"})
129
130
 
130
131
  return result
@@ -147,18 +148,12 @@ class AgentRun(BaseModel):
147
148
  # Generate transcript strings using appropriate method
148
149
  transcript_strs: list[str] = []
149
150
  for i, t in enumerate(self.transcripts):
150
- if use_blocks:
151
- transcript_content = t.to_str_blocks_with_token_limit(
152
- token_limit=sys.maxsize,
153
- transcript_idx=i,
154
- agent_run_idx=None,
155
- )[0]
156
- else:
157
- transcript_content = t.to_str_with_token_limit(
158
- token_limit=sys.maxsize,
159
- transcript_idx=i,
160
- agent_run_idx=None,
161
- )[0]
151
+ transcript_content = t.to_str(
152
+ token_limit=sys.maxsize,
153
+ transcript_idx=i,
154
+ agent_run_idx=None,
155
+ use_action_units=not use_blocks,
156
+ )[0]
162
157
  transcript_strs.append(f"<transcript>\n{transcript_content}\n</transcript>")
163
158
 
164
159
  transcripts_str = "\n\n".join(transcript_strs)
@@ -207,23 +202,16 @@ class AgentRun(BaseModel):
207
202
  ), "Ranges without metadata should be a single message"
208
203
  t = self.transcripts[msg_range.start]
209
204
  if msg_range.num_tokens < token_limit - 50:
210
- if use_blocks:
211
- transcript = f"<transcript>\n{t.to_str_blocks_with_token_limit(token_limit=sys.maxsize)[0]}\n</transcript>"
212
- else:
213
- transcript = f"<transcript>\n{t.to_str_with_token_limit(token_limit=sys.maxsize)[0]}\n</transcript>"
205
+ transcript = f"<transcript>\n{t.to_str(token_limit=sys.maxsize, use_action_units=not use_blocks)[0]}\n</transcript>"
214
206
  result = (
215
207
  f"Here is a partial agent run for analysis purposes only:\n{transcript}"
216
208
  )
217
209
  results.append(result)
218
210
  else:
219
- if use_blocks:
220
- transcript_fragments = t.to_str_blocks_with_token_limit(
221
- token_limit=token_limit - 50,
222
- )
223
- else:
224
- transcript_fragments = t.to_str_with_token_limit(
225
- token_limit=token_limit - 50,
226
- )
211
+ transcript_fragments = t.to_str(
212
+ token_limit=token_limit - 50,
213
+ use_action_units=not use_blocks,
214
+ )
227
215
  for fragment in transcript_fragments:
228
216
  result = f"<transcript>\n{fragment}\n</transcript>"
229
217
  result = (
@@ -459,10 +447,10 @@ class AgentRun(BaseModel):
459
447
  text = _recurse("__global_root")
460
448
 
461
449
  # Append agent run metadata below the full content
462
- yaml_text = yaml_dump_metadata(self.metadata)
463
- if yaml_text is not None:
450
+ metadata_text = dump_metadata(self.metadata)
451
+ if metadata_text is not None:
464
452
  if indent > 0:
465
- yaml_text = textwrap.indent(yaml_text, " " * indent)
466
- text += f"\n<|agent run metadata|>\n{yaml_text}\n</|agent run metadata|>"
453
+ metadata_text = textwrap.indent(metadata_text, " " * indent)
454
+ text += f"\n<|agent run metadata|>\n{metadata_text}\n</|agent run metadata|>"
467
455
 
468
456
  return text
@@ -7,7 +7,12 @@ from docent.data_models.chat.message import (
7
7
  UserMessage,
8
8
  parse_chat_message,
9
9
  )
10
- from docent.data_models.chat.tool import ToolCall, ToolCallContent, ToolInfo, ToolParams
10
+ from docent.data_models.chat.tool import (
11
+ ToolCall,
12
+ ToolCallContent,
13
+ ToolInfo,
14
+ ToolParams,
15
+ )
11
16
 
12
17
  __all__ = [
13
18
  "ChatMessage",
@@ -1,7 +1,7 @@
1
1
  from logging import getLogger
2
2
  from typing import Annotated, Any, Literal
3
3
 
4
- from pydantic import BaseModel, Discriminator
4
+ from pydantic import BaseModel, Discriminator, Field
5
5
 
6
6
  from docent.data_models.chat.content import Content
7
7
  from docent.data_models.chat.tool import ToolCall
@@ -17,11 +17,13 @@ class BaseChatMessage(BaseModel):
17
17
  id: Optional unique identifier for the message.
18
18
  content: The message content, either as a string or list of Content objects.
19
19
  role: The role of the message sender (system, user, assistant, tool).
20
+ metadata: Additional structured metadata about the message.
20
21
  """
21
22
 
22
23
  id: str | None = None
23
24
  content: str | list[Content]
24
25
  role: Literal["system", "user", "assistant", "tool"]
26
+ metadata: dict[str, Any] = Field(default_factory=dict)
25
27
 
26
28
  @property
27
29
  def text(self) -> str:
@@ -1,15 +1,27 @@
1
1
  import re
2
+ from dataclasses import dataclass
2
3
 
3
4
  from pydantic import BaseModel
4
5
 
5
6
 
7
+ @dataclass
8
+ class ParsedCitation:
9
+ """Represents a parsed citation before conversion to full Citation object."""
10
+
11
+ transcript_idx: int | None
12
+ block_idx: int | None
13
+ metadata_key: str | None = None
14
+ start_pattern: str | None = None
15
+
16
+
6
17
  class Citation(BaseModel):
7
18
  start_idx: int
8
19
  end_idx: int
9
20
  agent_run_idx: int | None = None
10
21
  transcript_idx: int | None = None
11
- block_idx: int
22
+ block_idx: int | None = None
12
23
  action_unit_idx: int | None = None
24
+ metadata_key: str | None = None
13
25
  start_pattern: str | None = None
14
26
 
15
27
 
@@ -17,6 +29,9 @@ RANGE_BEGIN = "<RANGE>"
17
29
  RANGE_END = "</RANGE>"
18
30
 
19
31
  _SINGLE_RE = re.compile(r"T(\d+)B(\d+)")
32
+ _METADATA_RE = re.compile(r"^M\.([^:]+)$") # [M.key]
33
+ _TRANSCRIPT_METADATA_RE = re.compile(r"^T(\d+)M\.([^:]+)$") # [T0M.key]
34
+ _MESSAGE_METADATA_RE = re.compile(r"^T(\d+)B(\d+)M\.([^:]+)$") # [T0B1M.key]
20
35
  _RANGE_CONTENT_RE = re.compile(r":\s*" + re.escape(RANGE_BEGIN) + r".*?" + re.escape(RANGE_END))
21
36
 
22
37
 
@@ -70,41 +85,93 @@ def scan_brackets(text: str) -> list[tuple[int, int, str]]:
70
85
  return matches
71
86
 
72
87
 
73
- def parse_single_citation(part: str) -> tuple[int, int, str | None] | None:
88
+ def parse_single_citation(part: str) -> ParsedCitation | None:
74
89
  """
75
90
  Parse a single citation token inside a bracket and return its components.
76
91
 
77
- Returns (transcript_idx, block_idx, start_pattern) or None if invalid.
92
+ Returns ParsedCitation or None if invalid.
93
+ For metadata citations, transcript_idx may be None (for agent run metadata).
94
+ Supports optional text range for all valid citation kinds.
78
95
  """
79
96
  token = part.strip()
80
97
  if not token:
81
98
  return None
82
99
 
100
+ # Extract optional range part
101
+ start_pattern: str | None = None
102
+ citation_part = token
83
103
  if ":" in token:
84
- citation_part, range_part = token.split(":", 1)
85
- single_match = _SINGLE_RE.match(citation_part.strip())
86
- if not single_match:
104
+ left, right = token.split(":", 1)
105
+ citation_part = left.strip()
106
+ start_pattern = _extract_range_pattern(right)
107
+
108
+ # Try matches in order of specificity
109
+ # 1) Message metadata [T0B0M.key]
110
+ m = _MESSAGE_METADATA_RE.match(citation_part)
111
+ if m:
112
+ transcript_idx = int(m.group(1))
113
+ block_idx = int(m.group(2))
114
+ metadata_key = m.group(3)
115
+ # Disallow nested keys like status.code per instruction
116
+ if "." in metadata_key:
87
117
  return None
88
- transcript_idx = int(single_match.group(1))
89
- block_idx = int(single_match.group(2))
90
- start_pattern = _extract_range_pattern(range_part)
91
- return transcript_idx, block_idx, start_pattern
92
- else:
93
- single_match = _SINGLE_RE.match(token)
94
- if not single_match:
118
+ return ParsedCitation(
119
+ transcript_idx=transcript_idx,
120
+ block_idx=block_idx,
121
+ metadata_key=metadata_key,
122
+ start_pattern=start_pattern,
123
+ )
124
+
125
+ # 2) Transcript metadata [T0M.key]
126
+ m = _TRANSCRIPT_METADATA_RE.match(citation_part)
127
+ if m:
128
+ transcript_idx = int(m.group(1))
129
+ metadata_key = m.group(2)
130
+ if "." in metadata_key:
95
131
  return None
96
- transcript_idx = int(single_match.group(1))
97
- block_idx = int(single_match.group(2))
98
- return transcript_idx, block_idx, None
132
+ return ParsedCitation(
133
+ transcript_idx=transcript_idx,
134
+ block_idx=None,
135
+ metadata_key=metadata_key,
136
+ start_pattern=start_pattern,
137
+ )
138
+
139
+ # 3) Agent run metadata [M.key]
140
+ m = _METADATA_RE.match(citation_part)
141
+ if m:
142
+ metadata_key = m.group(1)
143
+ if "." in metadata_key:
144
+ return None
145
+ return ParsedCitation(
146
+ transcript_idx=None,
147
+ block_idx=None,
148
+ metadata_key=metadata_key,
149
+ start_pattern=start_pattern,
150
+ )
151
+
152
+ # 4) Regular transcript block [T0B0]
153
+ m = _SINGLE_RE.match(citation_part)
154
+ if m:
155
+ transcript_idx = int(m.group(1))
156
+ block_idx = int(m.group(2))
157
+ return ParsedCitation(
158
+ transcript_idx=transcript_idx, block_idx=block_idx, start_pattern=start_pattern
159
+ )
160
+
161
+ return None
99
162
 
100
163
 
101
164
  def parse_citations(text: str) -> tuple[str, list[Citation]]:
102
165
  """
103
- Parse citations from text in the format described by BLOCK_RANGE_CITE_INSTRUCTION.
166
+ Parse citations from text in the format described by TEXT_RANGE_CITE_INSTRUCTION.
104
167
 
105
168
  Supported formats:
106
169
  - Single block: [T<key>B<idx>]
107
170
  - Text range with start pattern: [T<key>B<idx>:<RANGE>start_pattern</RANGE>]
171
+ - Agent run metadata: [M.key]
172
+ - Transcript metadata: [T<key>M.key]
173
+ - Message metadata: [T<key>B<idx>M.key]
174
+ - Message metadata with text range: [T<key>B<idx>M.key:<RANGE>start_pattern</RANGE>]
108
175
 
109
176
  Args:
110
177
  text: The text to parse citations from
@@ -127,8 +194,21 @@ def parse_citations(text: str) -> tuple[str, list[Citation]]:
127
194
  # Parse a single citation token inside the bracket
128
195
  parsed = parse_single_citation(bracket_content)
129
196
  if parsed:
130
- transcript_idx, block_idx, start_pattern = parsed
131
- replacement = f"T{transcript_idx}B{block_idx}"
197
+ # Create appropriate replacement text based on citation type
198
+ if parsed.metadata_key:
199
+ if parsed.transcript_idx is None:
200
+ # Agent run metadata [M.key]
201
+ replacement = "run metadata"
202
+ elif parsed.block_idx is None:
203
+ # Transcript metadata [T0M.key]
204
+ replacement = f"T{parsed.transcript_idx}"
205
+ else:
206
+ # Message metadata [T0B1M.key]
207
+ replacement = f"T{parsed.transcript_idx}B{parsed.block_idx}"
208
+ else:
209
+ # Regular transcript block [T0B1]
210
+ replacement = f"T{parsed.transcript_idx}B{parsed.block_idx}"
211
+
132
212
  # Current absolute start position for this replacement in the cleaned text
133
213
  start_idx = len(cleaned_text)
134
214
  end_idx = start_idx + len(replacement)
@@ -137,10 +217,11 @@ def parse_citations(text: str) -> tuple[str, list[Citation]]:
137
217
  start_idx=start_idx,
138
218
  end_idx=end_idx,
139
219
  agent_run_idx=None,
140
- transcript_idx=transcript_idx,
141
- block_idx=block_idx,
220
+ transcript_idx=parsed.transcript_idx,
221
+ block_idx=parsed.block_idx,
142
222
  action_unit_idx=None,
143
- start_pattern=start_pattern,
223
+ metadata_key=parsed.metadata_key,
224
+ start_pattern=parsed.start_pattern,
144
225
  )
145
226
  )
146
227
  cleaned_text += replacement
@@ -0,0 +1,19 @@
1
+ """Judge-related data models shared across Docent components."""
2
+
3
+ from typing import Any
4
+ from uuid import uuid4
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class Label(BaseModel):
10
+ id: str = Field(default_factory=lambda: str(uuid4()))
11
+
12
+ label_set_id: str
13
+
14
+ label_value: dict[str, Any]
15
+
16
+ agent_run_id: str
17
+
18
+
19
+ __all__ = ["Label"]
@@ -0,0 +1,16 @@
1
+ import json
2
+ from typing import Any
3
+
4
+ from pydantic_core import to_jsonable_python
5
+
6
+
7
+ def dump_metadata(metadata: dict[str, Any]) -> str | None:
8
+ """
9
+ Dump metadata to a JSON string.
10
+ We used to use YAML to save tokens, but JSON makes it easier to find cited ranges on the frontend because the frontend uses JSON.
11
+ """
12
+ if not metadata:
13
+ return None
14
+ metadata_obj = to_jsonable_python(metadata)
15
+ text = json.dumps(metadata_obj, indent=2)
16
+ return text.strip()
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import re
2
3
 
3
4
  from docent.data_models.agent_run import AgentRun
@@ -52,7 +53,7 @@ def find_citation_matches_in_text(text: str, start_pattern: str) -> list[tuple[i
52
53
 
53
54
  def get_transcript_text_for_citation(agent_run: AgentRun, citation: Citation) -> str | None:
54
55
  """
55
- Get the text content of a specific transcript block from an AgentRun,
56
+ Get the text content of a specific transcript block (or transcript/run metadata) from an AgentRun,
56
57
  using the same formatting as shown to LLMs via format_chat_message.
57
58
 
58
59
  Args:
@@ -62,19 +63,28 @@ def get_transcript_text_for_citation(agent_run: AgentRun, citation: Citation) ->
62
63
  Returns:
63
64
  Text content of the specified block (including tool calls), or None if not found
64
65
  """
65
- if citation.transcript_idx is None:
66
- return None
67
-
68
66
  try:
69
- if citation.transcript_idx >= len(agent_run.get_transcript_ids_ordered()):
67
+ if citation.transcript_idx is None:
68
+ # At the run level, can only cite metadata
69
+ if citation.metadata_key is not None:
70
+ return json.dumps(agent_run.metadata.get(citation.metadata_key))
70
71
  return None
72
+
71
73
  transcript_id = agent_run.get_transcript_ids_ordered()[citation.transcript_idx]
72
74
  transcript = agent_run.transcript_dict[transcript_id]
73
75
 
74
- if citation.block_idx >= len(transcript.messages):
76
+ if citation.block_idx is None:
77
+ # At the transcript level, can only cite metadata
78
+ if citation.metadata_key is not None:
79
+ return json.dumps(transcript.metadata.get(citation.metadata_key))
75
80
  return None
81
+
76
82
  message = transcript.messages[citation.block_idx]
77
83
 
84
+ # At the message level, can cite metadata or content
85
+ if citation.metadata_key is not None:
86
+ return json.dumps(message.metadata.get(citation.metadata_key))
87
+
78
88
  # Use the same formatting function that generates content for LLMs
79
89
  # This ensures consistent formatting between citation validation and LLM serialization
80
90
  return format_chat_message(
@@ -99,6 +109,9 @@ def validate_citation_text_range(agent_run: AgentRun, citation: Citation) -> boo
99
109
  if not citation.start_pattern:
100
110
  # Nothing to validate
101
111
  return True
112
+ if citation.metadata_key is not None:
113
+ # We don't need to remove invalid metadata citation ranges
114
+ return True
102
115
 
103
116
  text = get_transcript_text_for_citation(agent_run, citation)
104
117
  if text is None:
@@ -130,16 +143,16 @@ def remove_invalid_citation_ranges(text: str, agent_run: AgentRun) -> str:
130
143
  # Parse this bracket content to get citation info
131
144
  parsed = parse_single_citation(bracket_content)
132
145
  if parsed:
133
- transcript_idx, block_idx, start_pattern = parsed
134
146
  # The citation spans from start to end in the original text
135
147
  citation = Citation(
136
148
  start_idx=start,
137
149
  end_idx=end,
138
150
  agent_run_idx=None,
139
- transcript_idx=transcript_idx,
140
- block_idx=block_idx,
151
+ transcript_idx=parsed.transcript_idx,
152
+ block_idx=parsed.block_idx,
141
153
  action_unit_idx=None,
142
- start_pattern=start_pattern,
154
+ metadata_key=parsed.metadata_key,
155
+ start_pattern=parsed.start_pattern,
143
156
  )
144
157
  citations.append(citation)
145
158
 
@@ -15,7 +15,7 @@ from docent.data_models._tiktoken_util import (
15
15
  )
16
16
  from docent.data_models.chat import AssistantMessage, ChatMessage, ContentReasoning
17
17
  from docent.data_models.citation import RANGE_BEGIN, RANGE_END
18
- from docent.data_models.yaml_util import yaml_dump_metadata
18
+ from docent.data_models.metadata_util import dump_metadata
19
19
 
20
20
  # Template for formatting individual transcript blocks
21
21
  TRANSCRIPT_BLOCK_TEMPLATE = """
@@ -29,6 +29,12 @@ TEXT_RANGE_CITE_INSTRUCTION = f"""Anytime you quote the transcript, or refer to
29
29
 
30
30
  A citation may include a specific range of text within a block. Use {RANGE_BEGIN} and {RANGE_END} to mark the specific range of text. Add it after the block ID separated by a colon. For example, to cite the part of transcript 0, block 1, where the agent says "I understand the task", write [T0B1:{RANGE_BEGIN}I understand the task{RANGE_END}]. Citations must follow this exact format. The markers {RANGE_BEGIN} and {RANGE_END} must be used ONLY inside the brackets of a citation.
31
31
 
32
+ - You may cite a top-level key in the agent run metadata like this: [M.task_description].
33
+ - You may cite a top-level key in transcript metadata. For example, for transcript 0: [T0M.start_time].
34
+ - You may cite a top-level key in message metadata for a block. For example, for transcript 0, block 1: [T0B1M.status].
35
+ - You may not cite nested keys. For example, [T0B1M.status.code] is invalid.
36
+ - Within a top-level metadata key you may cite a range of text that appears in the value. For example, [T0B1M.status:{RANGE_BEGIN}"running":false{RANGE_END}].
37
+
32
38
  Important notes:
33
39
  - You must include the full content of the text range {RANGE_BEGIN} and {RANGE_END}, EXACTLY as it appears in the transcript, word-for-word, including any markers or punctuation that appear in the middle of the text.
34
40
  - Citations must be as specific as possible. This means you should usually cite a specific text range within a block.
@@ -36,6 +42,8 @@ Important notes:
36
42
  - Citations are self-contained. Do NOT label them as citation or evidence. Just insert the citation by itself at the appropriate place in the text.
37
43
  - Citations must come immediately after the part of a claim that they support. This may be in the middle of a sentence.
38
44
  - Each pair of brackets must contain only one citation. To cite multiple blocks, use multiple pairs of brackets, like [T0B0] [T0B1].
45
+ - Outside of citations, do not refer to transcript numbers or block numbers.
46
+ - Outside of citations, avoid quoting or paraphrasing the transcript.
39
47
  """
40
48
 
41
49
  BLOCK_CITE_INSTRUCTION = """Each transcript and each block has a unique index. Cite the relevant indices in brackets when relevant, like [T<idx>B<idx>]. Use multiple tags to cite multiple blocks, like [T<idx1>B<idx1>][T<idx2>B<idx2>]. Remember to cite specific blocks and NOT action units."""
@@ -72,6 +80,11 @@ def format_chat_message(
72
80
  args = ", ".join([f"{k}={v}" for k, v in tool_call.arguments.items()])
73
81
  cur_content += f"\n<tool call>\n{tool_call.function}({args})\n</tool call>"
74
82
 
83
+ if message.metadata:
84
+ metadata_text = dump_metadata(message.metadata)
85
+ if metadata_text is not None:
86
+ cur_content += f"\n<|message metadata|>\n{metadata_text}\n</|message metadata|>"
87
+
75
88
  return TRANSCRIPT_BLOCK_TEMPLATE.format(
76
89
  index_label=index_label, role=message.role, content=cur_content
77
90
  )
@@ -122,13 +135,11 @@ class TranscriptGroup(BaseModel):
122
135
  str: XML-like wrapped text including the group's metadata.
123
136
  """
124
137
  # Prepare YAML metadata
125
- yaml_text = yaml_dump_metadata(self.metadata)
126
- if yaml_text is not None:
138
+ metadata_text = dump_metadata(self.metadata)
139
+ if metadata_text is not None:
127
140
  if indent > 0:
128
- yaml_text = textwrap.indent(yaml_text, " " * indent)
129
- inner = (
130
- f"{children_text}\n<|{self.name} metadata|>\n{yaml_text}\n</|{self.name} metadata|>"
131
- )
141
+ metadata_text = textwrap.indent(metadata_text, " " * indent)
142
+ inner = f"{children_text}\n<|{self.name} metadata|>\n{metadata_text}\n</|{self.name} metadata|>"
132
143
  else:
133
144
  inner = children_text
134
145
 
@@ -301,20 +312,6 @@ class Transcript(BaseModel):
301
312
  self.messages = messages
302
313
  self._units_of_action = self._compute_units_of_action()
303
314
 
304
- def to_str(
305
- self,
306
- transcript_idx: int = 0,
307
- agent_run_idx: int | None = None,
308
- highlight_action_unit: int | None = None,
309
- ) -> str:
310
- return self._to_str_with_token_limit_impl(
311
- token_limit=sys.maxsize,
312
- transcript_idx=transcript_idx,
313
- agent_run_idx=agent_run_idx,
314
- use_action_units=True,
315
- highlight_action_unit=highlight_action_unit,
316
- )[0]
317
-
318
315
  def _generate_formatted_blocks(
319
316
  self,
320
317
  transcript_idx: int = 0,
@@ -379,9 +376,9 @@ class Transcript(BaseModel):
379
376
 
380
377
  return blocks
381
378
 
382
- def _to_str_with_token_limit_impl(
379
+ def to_str(
383
380
  self,
384
- token_limit: int,
381
+ token_limit: int = sys.maxsize,
385
382
  transcript_idx: int = 0,
386
383
  agent_run_idx: int | None = None,
387
384
  use_action_units: bool = True,
@@ -408,7 +405,7 @@ class Transcript(BaseModel):
408
405
  metadata_obj = to_jsonable_python(self.metadata)
409
406
  yaml_width = float("inf")
410
407
  block_str = f"<blocks>\n{blocks_str}\n</blocks>\n"
411
- metadata_str = f"<metadata>\n{yaml.dump(metadata_obj, width=yaml_width)}\n</metadata>"
408
+ metadata_str = f"<|transcript metadata|>\n{yaml.dump(metadata_obj, width=yaml_width)}\n</|transcript metadata|>"
412
409
 
413
410
  if token_limit == sys.maxsize:
414
411
  return [f"{block_str}" f"{metadata_str}"]
@@ -439,56 +436,6 @@ class Transcript(BaseModel):
439
436
 
440
437
  return results
441
438
 
442
- def to_str_blocks(
443
- self,
444
- transcript_idx: int = 0,
445
- agent_run_idx: int | None = None,
446
- ) -> str:
447
- """Represents the transcript as a string using individual message blocks.
448
-
449
- Unlike to_str() which groups messages into action units, this method
450
- formats each message as an individual block.
451
-
452
- Returns:
453
- str: A string representation with individual message blocks.
454
- """
455
- return self._to_str_with_token_limit_impl(
456
- token_limit=sys.maxsize,
457
- transcript_idx=transcript_idx,
458
- agent_run_idx=agent_run_idx,
459
- use_action_units=False,
460
- )[0]
461
-
462
- def to_str_with_token_limit(
463
- self,
464
- token_limit: int,
465
- transcript_idx: int = 0,
466
- agent_run_idx: int | None = None,
467
- highlight_action_unit: int | None = None,
468
- ) -> list[str]:
469
- """Represents the transcript as a list of strings using action units with token limit handling."""
470
- return self._to_str_with_token_limit_impl(
471
- token_limit=token_limit,
472
- transcript_idx=transcript_idx,
473
- agent_run_idx=agent_run_idx,
474
- use_action_units=True,
475
- highlight_action_unit=highlight_action_unit,
476
- )
477
-
478
- def to_str_blocks_with_token_limit(
479
- self,
480
- token_limit: int,
481
- transcript_idx: int = 0,
482
- agent_run_idx: int | None = None,
483
- ) -> list[str]:
484
- """Represents the transcript as individual blocks with token limit handling."""
485
- return self._to_str_with_token_limit_impl(
486
- token_limit=token_limit,
487
- transcript_idx=transcript_idx,
488
- agent_run_idx=agent_run_idx,
489
- use_action_units=False,
490
- )
491
-
492
439
  ##############################
493
440
  # New text rendering methods #
494
441
  ##############################
@@ -506,13 +453,11 @@ class Transcript(BaseModel):
506
453
  content_str = f"<|T{transcript_idx} blocks|>\n{blocks_str}\n</|T{transcript_idx} blocks|>"
507
454
 
508
455
  # Gather metadata and add to content
509
- yaml_text = yaml_dump_metadata(self.metadata)
510
- if yaml_text is not None:
456
+ metadata_text = dump_metadata(self.metadata)
457
+ if metadata_text is not None:
511
458
  if indent > 0:
512
- yaml_text = textwrap.indent(yaml_text, " " * indent)
513
- content_str += (
514
- f"\n<|T{transcript_idx} metadata|>\n{yaml_text}\n</|T{transcript_idx} metadata|>"
515
- )
459
+ metadata_text = textwrap.indent(metadata_text, " " * indent)
460
+ content_str += f"\n<|T{transcript_idx} metadata|>\n{metadata_text}\n</|T{transcript_idx} metadata|>"
516
461
 
517
462
  # Format content and return
518
463
  if indent > 0: