Jac-Zac commited on
Commit
88f2164
·
1 Parent(s): 610d258

Updated to lates versions

Browse files
Files changed (5) hide show
  1. pyproject.toml +2 -2
  2. tabs/extract.py +16 -45
  3. utils/chat.py +6 -69
  4. utils/contrast.py +4 -2
  5. uv.lock +17 -17
pyproject.toml CHANGED
@@ -5,8 +5,8 @@ description = "Streamlit UI for persona-vectors"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
- "persona-vectors>=0.4.2",
9
- "persona-data>=0.2.5",
10
  "streamlit>=1.44.0",
11
  "plotly>=6.6.0",
12
  "python-dotenv>=1.2.2",
 
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
+ "persona-vectors>=0.4.3",
9
+ "persona-data>=0.2.7",
10
  "streamlit>=1.44.0",
11
  "plotly>=6.6.0",
12
  "python-dotenv>=1.2.2",
tabs/extract.py CHANGED
@@ -5,14 +5,15 @@ import streamlit as st
5
  from persona_data.prompts import (
6
  BASELINE_PERSONA_ID,
7
  BASELINE_PERSONA_NAME,
8
- format_roleplay_prompt,
9
  )
10
  from persona_data.synth_persona import PersonaData, QAPair
11
  from persona_vectors.artifacts import PERSONA_VARIANTS
12
  from persona_vectors.extraction import (
13
  MaskStrategy,
14
- PreparedInput,
15
  prepare_inputs_for_strategy,
 
16
  run_extraction,
17
  )
18
 
@@ -77,54 +78,24 @@ _TOKEN_LEGEND = (
77
  _MAX_PREVIEW_SAMPLES = 3
78
 
79
 
80
- def _token_style_for_index(
81
- p: PreparedInput, token_idx: int, special_ids: set[int]
82
- ) -> str:
83
- if p.spans.response.token_start <= token_idx < p.spans.response.token_end:
84
- style = "color:#22d3ee"
85
- elif p.spans.question.token_start <= token_idx < p.spans.question.token_end:
86
- style = "color:#fde047"
87
- else:
88
- style = "color:#9ca3af"
89
-
90
- if int(p.input_ids[token_idx]) in special_ids:
91
  style = "color:#d946ef;font-weight:bold"
92
- if p.token_mask[token_idx]:
93
  style = f"{style};background:#86efac;border-radius:2px;padding:0 1px"
94
  return style
95
 
96
 
97
- def _render_sample_tokens_html(
98
- p: PreparedInput, tokenizer, *, max_tokens: int = 200
99
- ) -> str:
100
- """Build an HTML token sequence using the persona-vectors preview layout."""
101
- special_ids = set(tokenizer.all_special_ids)
102
- seq_len = int(p.input_ids.shape[0])
103
- edge = max(8, max_tokens // 4)
104
-
105
- prefix_end = min(p.spans.template.token_start + max_tokens, seq_len)
106
- tail_start = min(max(prefix_end, p.spans.template.token_end - edge), seq_len)
107
- answer_end = min(seq_len, p.spans.response.token_end + edge)
108
-
109
- indices: list[int | None] = list(range(0, prefix_end))
110
- if prefix_end < tail_start:
111
- indices.append(None)
112
- indices.extend(range(tail_start, answer_end))
113
- if answer_end < seq_len:
114
- indices.append(None)
115
-
116
  spans: list[str] = []
117
- for idx in indices:
118
- if idx is None:
119
- spans.append('<span style="color:#9ca3af"> … </span>')
120
- continue
121
- char_start, char_end = p.offset_mapping[idx]
122
- raw = p.prompt_text[char_start:char_end]
123
- if not raw:
124
- raw = tokenizer.convert_ids_to_tokens([int(p.input_ids[idx])])[0]
125
- escaped = html.escape(raw)
126
  spans.append(
127
- f'<span style="{_token_style_for_index(p, idx, special_ids)}">{escaped}</span>'
128
  )
129
 
130
  return (
@@ -357,9 +328,9 @@ def render_extract_tab(remote: bool, model_name: str, dataset_source: str) -> No
357
  st.markdown(_TOKEN_LEGEND, unsafe_allow_html=True)
358
  for persona, qa_pairs, variant in run_plan:
359
  system_prompt = (
360
- format_roleplay_prompt()
361
  if persona is None
362
- else format_roleplay_prompt(getattr(persona, f"{variant}_view"))
363
  )
364
  prepared = prepare_inputs_for_strategy(
365
  tokenizer=model.tokenizer,
 
5
  from persona_data.prompts import (
6
  BASELINE_PERSONA_ID,
7
  BASELINE_PERSONA_NAME,
8
+ format_prompt,
9
  )
10
  from persona_data.synth_persona import PersonaData, QAPair
11
  from persona_vectors.artifacts import PERSONA_VARIANTS
12
  from persona_vectors.extraction import (
13
  MaskStrategy,
14
+ TokenSegment,
15
  prepare_inputs_for_strategy,
16
+ preview_token_segments,
17
  run_extraction,
18
  )
19
 
 
78
  _MAX_PREVIEW_SAMPLES = 3
79
 
80
 
81
+ def _token_style(segment: TokenSegment) -> str:
82
+ style = {
83
+ "response": "color:#22d3ee",
84
+ "question": "color:#fde047",
85
+ }.get(segment.role, "color:#9ca3af")
86
+
87
+ if segment.is_special:
 
 
 
 
88
  style = "color:#d946ef;font-weight:bold"
89
+ if segment.is_masked:
90
  style = f"{style};background:#86efac;border-radius:2px;padding:0 1px"
91
  return style
92
 
93
 
94
+ def _render_sample_tokens_html(p, tokenizer, *, max_tokens: int = 200) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  spans: list[str] = []
96
+ for segment in preview_token_segments(p, tokenizer, max_tokens=max_tokens):
 
 
 
 
 
 
 
 
97
  spans.append(
98
+ f'<span style="{_token_style(segment)}">{html.escape(segment.text)}</span>'
99
  )
100
 
101
  return (
 
328
  st.markdown(_TOKEN_LEGEND, unsafe_allow_html=True)
329
  for persona, qa_pairs, variant in run_plan:
330
  system_prompt = (
331
+ format_prompt()
332
  if persona is None
333
+ else format_prompt(persona, variant) # type: ignore[arg-type]
334
  )
335
  prepared = prepare_inputs_for_strategy(
336
  tokenizer=model.tokenizer,
utils/chat.py CHANGED
@@ -1,14 +1,12 @@
1
- import logging
2
  from contextlib import contextmanager, nullcontext
3
  from dataclasses import dataclass
4
  from typing import Literal
5
 
6
  import torch
7
  from nnterp import StandardizedTransformer
8
- from persona_data.prompts import _normalize_messages, format_roleplay_prompt
9
  from persona_data.synth_persona import PersonaData
10
 
11
- logger = logging.getLogger(__name__)
12
  SystemPromptMode = Literal["empty", "templated", "biography", "custom"]
13
 
14
 
@@ -36,75 +34,12 @@ def resolve_system_prompt(
36
  if persona is None or mode == "empty":
37
  return ""
38
  if mode == "custom":
39
- return format_roleplay_prompt(mode="conversational")
40
  if mode in ("templated", "biography"):
41
- return format_roleplay_prompt(
42
- getattr(persona, f"{mode}_view"), mode="conversational"
43
- )
44
  raise ValueError(f"Unsupported system prompt mode: {mode}")
45
 
46
 
47
- def _format_plain_messages(messages: list[dict[str, str]]) -> str:
48
- """Format messages as plain ``Role: content`` text, used as a last-resort fallback."""
49
- lines: list[str] = []
50
-
51
- for message in messages:
52
- role = message["role"]
53
- content = message["content"]
54
-
55
- if role == "system":
56
- if content:
57
- lines.append(f"System: {content}")
58
- elif role == "user":
59
- lines.append(f"User: {content}")
60
- elif role == "assistant":
61
- lines.append(f"Assistant: {content}")
62
- else:
63
- lines.append(f"{role.title()}: {content}")
64
-
65
- if not lines or not lines[-1].startswith("Assistant:"):
66
- lines.append("Assistant:")
67
-
68
- return "\n\n".join(lines)
69
-
70
-
71
- def format_generation_prompt(
72
- messages: list[dict[str, str]], tokenizer: object
73
- ) -> tuple[str, int]:
74
- """Render messages into a single prompt string and count prompt tokens.
75
-
76
- Tries the tokenizer's chat template first, falls back to normalized messages,
77
- then to a plain-text format if both template attempts fail.
78
- """
79
- try:
80
- prompt = tokenizer.apply_chat_template(
81
- messages,
82
- tokenize=False,
83
- add_generation_prompt=True,
84
- )
85
- except Exception:
86
- logger.debug(
87
- "Chat template failed on raw messages, trying normalized", exc_info=True
88
- )
89
- messages = _normalize_messages(messages)
90
-
91
- try:
92
- prompt = tokenizer.apply_chat_template(
93
- messages,
94
- tokenize=False,
95
- add_generation_prompt=True,
96
- )
97
- except Exception:
98
- logger.debug(
99
- "Chat template failed on normalized messages, falling back to plain format",
100
- exc_info=True,
101
- )
102
- prompt = _format_plain_messages(messages)
103
-
104
- prompt_token_count = tokenizer(prompt, return_tensors="pt").input_ids.shape[1]
105
- return prompt, prompt_token_count
106
-
107
-
108
  @contextmanager
109
  def _seeded_rng(seed: int | None):
110
  """Context manager that forks the RNG state and sets a deterministic seed."""
@@ -161,7 +96,9 @@ def generate_chat_reply(
161
  """
162
 
163
  tokenizer = model.tokenizer
164
- prompt, prompt_token_count = format_generation_prompt(messages, tokenizer)
 
 
165
 
166
  generation_kwargs: dict[str, object] = {
167
  "max_new_tokens": max_new_tokens,
 
 
1
  from contextlib import contextmanager, nullcontext
2
  from dataclasses import dataclass
3
  from typing import Literal
4
 
5
  import torch
6
  from nnterp import StandardizedTransformer
7
+ from persona_data.prompts import format_messages, format_prompt
8
  from persona_data.synth_persona import PersonaData
9
 
 
10
  SystemPromptMode = Literal["empty", "templated", "biography", "custom"]
11
 
12
 
 
34
  if persona is None or mode == "empty":
35
  return ""
36
  if mode == "custom":
37
+ return format_prompt(mode="conversational")
38
  if mode in ("templated", "biography"):
39
+ return format_prompt(persona, mode, mode="conversational")
 
 
40
  raise ValueError(f"Unsupported system prompt mode: {mode}")
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  @contextmanager
44
  def _seeded_rng(seed: int | None):
45
  """Context manager that forks the RNG state and sets a deterministic seed."""
 
96
  """
97
 
98
  tokenizer = model.tokenizer
99
+ prompt, prompt_token_count = format_messages(
100
+ messages, tokenizer, add_generation_prompt=True
101
+ )
102
 
103
  generation_kwargs: dict[str, object] = {
104
  "max_new_tokens": max_new_tokens,
utils/contrast.py CHANGED
@@ -18,7 +18,7 @@ from html import escape
18
  import torch
19
  from nnterp import StandardizedTransformer
20
 
21
- from utils.chat import format_generation_prompt
22
 
23
  logger = logging.getLogger(__name__)
24
 
@@ -79,7 +79,9 @@ def _prepare_trace_text(
79
  response_ids: torch.Tensor,
80
  ) -> tuple[str, int, int]:
81
  """Build the trace text and return ``(full_text, n_ctx, n_resp)``."""
82
- context_prompt, _ = format_generation_prompt(context_messages, tokenizer)
 
 
83
  context_ids = tokenizer(context_prompt, return_tensors="pt").input_ids[0]
84
  response_text = _decode_ids(tokenizer, response_ids.tolist())
85
  full_text = context_prompt + response_text
 
18
  import torch
19
  from nnterp import StandardizedTransformer
20
 
21
+ from persona_data.prompts import format_messages
22
 
23
  logger = logging.getLogger(__name__)
24
 
 
79
  response_ids: torch.Tensor,
80
  ) -> tuple[str, int, int]:
81
  """Build the trace text and return ``(full_text, n_ctx, n_resp)``."""
82
+ context_prompt, _ = format_messages(
83
+ context_messages, tokenizer, add_generation_prompt=True
84
+ )
85
  context_ids = tokenizer(context_prompt, return_tensors="pt").input_ids[0]
86
  response_text = _decode_ids(tokenizer, response_ids.tolist())
87
  full_text = context_prompt + response_text
uv.lock CHANGED
@@ -353,11 +353,11 @@ wheels = [
353
 
354
  [[package]]
355
  name = "fsspec"
356
- version = "2026.3.0"
357
  source = { registry = "https://pypi.org/simple" }
358
- sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" }
359
  wheels = [
360
- { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" },
361
  ]
362
 
363
  [[package]]
@@ -484,7 +484,7 @@ wheels = [
484
 
485
  [[package]]
486
  name = "huggingface-hub"
487
- version = "1.12.2"
488
  source = { registry = "https://pypi.org/simple" }
489
  dependencies = [
490
  { name = "filelock" },
@@ -497,9 +497,9 @@ dependencies = [
497
  { name = "typer" },
498
  { name = "typing-extensions" },
499
  ]
500
- sdist = { url = "https://files.pythonhosted.org/packages/3e/9f/3fda8b014db3ae239addc9b48b35c2cf7d318950b430712f34a2473ef81d/huggingface_hub-1.12.2.tar.gz", hash = "sha256:282c4999e641c89affdc4c02c265eddea944c1390dc19e89dac8ad3ae76dbdaf", size = 763393, upload-time = "2026-04-29T09:45:09.202Z" }
501
  wheels = [
502
- { url = "https://files.pythonhosted.org/packages/71/c1/1fa4162f6dd53259daf2ad31385273341821fa0acce164cd03971937a60e/huggingface_hub-1.12.2-py3-none-any.whl", hash = "sha256:7968e897fdbc6343c871c240d87d4434efe0ad9f80d57daa1cc5678c6d148529", size = 647757, upload-time = "2026-04-29T09:45:07.63Z" },
503
  ]
504
 
505
  [[package]]
@@ -1209,7 +1209,7 @@ wheels = [
1209
 
1210
  [[package]]
1211
  name = "persona-data"
1212
- version = "0.2.5"
1213
  source = { registry = "https://pypi.org/simple" }
1214
  dependencies = [
1215
  { name = "huggingface-hub" },
@@ -1218,9 +1218,9 @@ dependencies = [
1218
  { name = "python-dotenv" },
1219
  { name = "torch" },
1220
  ]
1221
- sdist = { url = "https://files.pythonhosted.org/packages/e2/93/694d05273378f142c188b8a4826f962c89d1f449ed5949806a98a3912647/persona_data-0.2.5.tar.gz", hash = "sha256:d20a55fe488d61034ab2449a460e744a99a3aaa3ead128e5fe832ea14eb31c51", size = 8546, upload-time = "2026-04-29T09:25:59.28Z" }
1222
  wheels = [
1223
- { url = "https://files.pythonhosted.org/packages/de/06/6f81542a68c97fbbc6fabae7b4f406b046c61787977f623199172292bd5e/persona_data-0.2.5-py3-none-any.whl", hash = "sha256:70e8caa15ab31e8a2bd1e248d02acf118a43725acd262b10b594ee754986f2a9", size = 11199, upload-time = "2026-04-29T09:25:58.119Z" },
1224
  ]
1225
 
1226
  [[package]]
@@ -1238,8 +1238,8 @@ dependencies = [
1238
 
1239
  [package.metadata]
1240
  requires-dist = [
1241
- { name = "persona-data", specifier = ">=0.2.5" },
1242
- { name = "persona-vectors", specifier = ">=0.4.2" },
1243
  { name = "plotly", specifier = ">=6.6.0" },
1244
  { name = "python-dotenv", specifier = ">=1.2.2" },
1245
  { name = "streamlit", specifier = ">=1.44.0" },
@@ -1248,7 +1248,7 @@ requires-dist = [
1248
 
1249
  [[package]]
1250
  name = "persona-vectors"
1251
- version = "0.4.2"
1252
  source = { registry = "https://pypi.org/simple" }
1253
  dependencies = [
1254
  { name = "kaleido" },
@@ -1265,9 +1265,9 @@ dependencies = [
1265
  { name = "transformers" },
1266
  { name = "umap-learn" },
1267
  ]
1268
- sdist = { url = "https://files.pythonhosted.org/packages/01/48/e9ea34cd42213868b0e569bc25045f4098ab0fe483421f4eed3f4204f91d/persona_vectors-0.4.2.tar.gz", hash = "sha256:5c421ce6904cf7c92b8fb9ecf27a95d48a381ea307dae3dd67db54c11e9c0892", size = 21509, upload-time = "2026-04-29T14:24:00.304Z" }
1269
  wheels = [
1270
- { url = "https://files.pythonhosted.org/packages/6b/2f/be21db62788f9880bba713412ee8ad04d9388d749346437c445d83f5bbfb/persona_vectors-0.4.2-py3-none-any.whl", hash = "sha256:c672b91e7b34d02e8ec967e698bcd3b307305cdce02322e9f8e047f9984e264e", size = 25441, upload-time = "2026-04-29T14:23:59.129Z" },
1271
  ]
1272
 
1273
  [[package]]
@@ -2462,7 +2462,7 @@ wheels = [
2462
 
2463
  [[package]]
2464
  name = "typer"
2465
- version = "0.25.0"
2466
  source = { registry = "https://pypi.org/simple" }
2467
  dependencies = [
2468
  { name = "annotated-doc" },
@@ -2470,9 +2470,9 @@ dependencies = [
2470
  { name = "rich" },
2471
  { name = "shellingham" },
2472
  ]
2473
- sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150, upload-time = "2026-04-26T08:46:14.767Z" }
2474
  wheels = [
2475
- { url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993, upload-time = "2026-04-26T08:46:15.889Z" },
2476
  ]
2477
 
2478
  [[package]]
 
353
 
354
  [[package]]
355
  name = "fsspec"
356
+ version = "2026.4.0"
357
  source = { registry = "https://pypi.org/simple" }
358
+ sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" }
359
  wheels = [
360
+ { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" },
361
  ]
362
 
363
  [[package]]
 
484
 
485
  [[package]]
486
  name = "huggingface-hub"
487
+ version = "1.13.0"
488
  source = { registry = "https://pypi.org/simple" }
489
  dependencies = [
490
  { name = "filelock" },
 
497
  { name = "typer" },
498
  { name = "typing-extensions" },
499
  ]
500
+ sdist = { url = "https://files.pythonhosted.org/packages/89/ff/ec7ed2eb43bd7ce8bb2233d109cc235c3e807ffe5e469dc09db261fac05e/huggingface_hub-1.13.0.tar.gz", hash = "sha256:f6df2dac5abe82ce2fe05873d10d5ff47bc677d616a2f521f4ee26db9415d9d0", size = 781788, upload-time = "2026-04-30T11:57:33.858Z" }
501
  wheels = [
502
+ { url = "https://files.pythonhosted.org/packages/93/db/4b1cdae9460ae1f3ca020cd767f013430ce23eb1d9c890ae3a0609b38d26/huggingface_hub-1.13.0-py3-none-any.whl", hash = "sha256:e942cb50d6a08dd5306688b1ac05bda157fd2fcc88b63dae405f7bd0d3234005", size = 660643, upload-time = "2026-04-30T11:57:31.802Z" },
503
  ]
504
 
505
  [[package]]
 
1209
 
1210
  [[package]]
1211
  name = "persona-data"
1212
+ version = "0.2.7"
1213
  source = { registry = "https://pypi.org/simple" }
1214
  dependencies = [
1215
  { name = "huggingface-hub" },
 
1218
  { name = "python-dotenv" },
1219
  { name = "torch" },
1220
  ]
1221
+ sdist = { url = "https://files.pythonhosted.org/packages/e9/c1/8612da5d216e484f823333fcf6bc1ff97c266c800b597d70f9a19475b78e/persona_data-0.2.7.tar.gz", hash = "sha256:6cd95f82d628a2add68df3935ae47c36a13ab17bafaf495892be5b1192f4693e", size = 9143, upload-time = "2026-05-01T08:54:26.603Z" }
1222
  wheels = [
1223
+ { url = "https://files.pythonhosted.org/packages/b0/ad/65fff568431ae5620f15d5b9c1dff9a941fc77f8c6ebb6f3131c9010d713/persona_data-0.2.7-py3-none-any.whl", hash = "sha256:4395f46381d54e7047bd26a4eaa2e9db1bd29f80e00a813b8be3ede0192aafcb", size = 11832, upload-time = "2026-05-01T08:54:25.778Z" },
1224
  ]
1225
 
1226
  [[package]]
 
1238
 
1239
  [package.metadata]
1240
  requires-dist = [
1241
+ { name = "persona-data", specifier = ">=0.2.7" },
1242
+ { name = "persona-vectors", specifier = ">=0.4.3" },
1243
  { name = "plotly", specifier = ">=6.6.0" },
1244
  { name = "python-dotenv", specifier = ">=1.2.2" },
1245
  { name = "streamlit", specifier = ">=1.44.0" },
 
1248
 
1249
  [[package]]
1250
  name = "persona-vectors"
1251
+ version = "0.4.3"
1252
  source = { registry = "https://pypi.org/simple" }
1253
  dependencies = [
1254
  { name = "kaleido" },
 
1265
  { name = "transformers" },
1266
  { name = "umap-learn" },
1267
  ]
1268
+ sdist = { url = "https://files.pythonhosted.org/packages/15/97/5f89f74a98faec09696108a1b2222a5d0bc99c129e035939ba8a70a61314/persona_vectors-0.4.3.tar.gz", hash = "sha256:3b86bed93373868506f1f6006f65e849d560bd20fb771310a33a674f02a90b0b", size = 21614, upload-time = "2026-05-01T16:49:11.757Z" }
1269
  wheels = [
1270
+ { url = "https://files.pythonhosted.org/packages/92/de/a666ee51901db501556e9f7014e7f61741df8f2b219ff676179b80e94cd4/persona_vectors-0.4.3-py3-none-any.whl", hash = "sha256:50bfb2de68613ec89a87f5773f4d2f5fe9981e2949f82e4823cd4f4ca093bee3", size = 25563, upload-time = "2026-05-01T16:49:12.694Z" },
1271
  ]
1272
 
1273
  [[package]]
 
2462
 
2463
  [[package]]
2464
  name = "typer"
2465
+ version = "0.25.1"
2466
  source = { registry = "https://pypi.org/simple" }
2467
  dependencies = [
2468
  { name = "annotated-doc" },
 
2470
  { name = "rich" },
2471
  { name = "shellingham" },
2472
  ]
2473
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" }
2474
  wheels = [
2475
+ { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" },
2476
  ]
2477
 
2478
  [[package]]