Skip to content

Commit 692cf44

Browse files
penny-team[bot]jaredlockhartclaude
authored
Bump ty to 0.0.25 and fix all type checker errors (jaredlockhart#860)
ty 0.0.25 introduced stricter checking. Changes: - Replace `**kwargs: object` with `**kwargs: Any` in agent subclasses (HistoryAgent, ThinkingAgent, NotifyAgent, ScheduleExecutor) - Narrow `raise last_error` with assert in OllamaClient retry loops - Add assert narrowing for optional command_registry and SQLModel IDs - Fix dict.get type errors in search.py quota error detection - Downgrade unresolved-attribute to warn in ty config (SQLModel column descriptor methods like .desc()/.asc()/.in_() are false positives) - Add ty: ignore for SQLModel order_by() false positives - Remove 37 stale `# type: ignore[unresolved-attribute]` comments from database stores (now handled by ty rule config) Co-authored-by: Jared Lockhart <119884+jaredlockhart@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6accabb commit 692cf44

20 files changed

Lines changed: 82 additions & 73 deletions

penny-team/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ packages = []
2222
[dependency-groups]
2323
dev = [
2424
"ruff>=0.8.0",
25-
"ty>=0.0.1,<0.0.25",
25+
"ty>=0.0.25",
2626
"pytest>=8.0.0",
2727
]
2828

penny/penny/agents/history.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import logging
1212
from datetime import UTC, datetime, timedelta
13+
from typing import Any
1314

1415
from pydantic import BaseModel
1516
from pydantic import Field as PydanticField
@@ -77,9 +78,9 @@ class HistoryAgent(Agent):
7778
def get_max_steps(self) -> int:
7879
return PennyConstants.HISTORY_MAX_STEPS
7980

80-
def __init__(self, **kwargs: object) -> None:
81+
def __init__(self, **kwargs: Any) -> None:
8182
kwargs["system_prompt"] = Prompt.SUMMARIZE_TO_BULLETS
82-
super().__init__(**kwargs) # type: ignore[arg-type]
83+
super().__init__(**kwargs)
8384

8485
async def _build_system_prompt(self, user: str) -> str:
8586
"""Instructions only — no identity, profile, history, thoughts, or dislikes.
@@ -433,8 +434,9 @@ def _handle_dedup_match(
433434
if matched.id in already_bumped:
434435
logger.debug("Skipping already-bumped preference: '%s'", matched.content[:50])
435436
return
436-
self.db.preferences.increment_mention_count(matched.id) # type: ignore[arg-type]
437-
already_bumped.add(matched.id) # type: ignore[arg-type]
437+
assert matched.id is not None
438+
self.db.preferences.increment_mention_count(matched.id)
439+
already_bumped.add(matched.id)
438440
logger.info(
439441
"Preference '%s' mention count incremented (matches '%s')",
440442
topic[:50],

penny/penny/agents/notify.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import random
1717
import re
1818
from datetime import UTC, datetime
19-
from typing import TYPE_CHECKING
19+
from typing import TYPE_CHECKING, Any
2020

2121
from pydantic import BaseModel, Field
2222

@@ -275,8 +275,8 @@ def get_max_steps(self) -> int:
275275
"""Read from config so /config changes take effect immediately."""
276276
return int(self.config.runtime.MESSAGE_MAX_STEPS)
277277

278-
def __init__(self, **kwargs: object) -> None:
279-
super().__init__(**kwargs) # type: ignore[arg-type]
278+
def __init__(self, **kwargs: Any) -> None:
279+
super().__init__(**kwargs)
280280
self._boot_time = datetime.now(UTC).replace(tzinfo=None)
281281
self._channel: MessageChannel | None = None
282282
self._pending_thought: Thought | None = None

penny/penny/agents/thinking.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import logging
1111
import random
12+
from typing import Any
1213

1314
from penny.agents.base import Agent
1415
from penny.agents.models import ChatMessage, MessageRole
@@ -50,9 +51,9 @@ class ThinkingAgent(Agent):
5051

5152
name = "inner_monologue"
5253

53-
def __init__(self, **kwargs: object) -> None:
54+
def __init__(self, **kwargs: Any) -> None:
5455
kwargs["system_prompt"] = Prompt.THINKING_SYSTEM_PROMPT
55-
super().__init__(**kwargs) # type: ignore[arg-type]
56+
super().__init__(**kwargs)
5657
self._keep_tools_on_final_step = True
5758
self._seed_topic: str | None = None
5859
self._seed_pref_id: int | None = None

penny/penny/channels/base.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,14 +499,16 @@ async def _execute_command(
499499
self, message: IncomingMessage, command_name: str, command_args: str
500500
) -> None:
501501
"""Execute a known command with typing indicator and send the result."""
502-
command = self._command_registry.get(command_name) # type: ignore[union-attr]
502+
assert self._command_registry is not None
503+
command = self._command_registry.get(command_name)
503504
typing_task = asyncio.create_task(self._typing_loop(message.sender))
504505
try:
505506
context = self._command_context
506507
context.user = message.sender
507508
context.message = message
508509

509-
result = await command.execute(command_args, context) # type: ignore[union-attr]
510+
assert command is not None
511+
result = await command.execute(command_args, context)
510512
response = result.text
511513

512514
prepared = self.prepare_outgoing(response) if response else ""

penny/penny/commands/preference_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ def _delete_preference(
104104
)
105105

106106
to_delete = prefs[position - 1]
107-
context.db.preferences.delete(to_delete.id) # type: ignore[arg-type]
107+
assert to_delete.id is not None
108+
context.db.preferences.delete(to_delete.id)
108109

109110
remaining = [p for p in prefs if p.id != to_delete.id]
110111
label = self.valence_config.label

penny/penny/commands/schedule.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async def _list_schedules(self, context: CommandContext) -> CommandResult:
5353
with Session(context.db.engine) as session:
5454
schedules = list(
5555
session.exec(
56-
select(Schedule).where(Schedule.user_id == context.user).order_by(Schedule.id) # type: ignore[arg-type]
56+
select(Schedule).where(Schedule.user_id == context.user).order_by(Schedule.id) # ty: ignore[invalid-argument-type]
5757
)
5858
)
5959

penny/penny/commands/unschedule.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ async def execute(self, args: str, context: CommandContext) -> CommandResult:
3232
with Session(context.db.engine) as session:
3333
schedules = list(
3434
session.exec(
35-
select(Schedule).where(Schedule.user_id == context.user).order_by(Schedule.id) # type: ignore[arg-type]
35+
select(Schedule).where(Schedule.user_id == context.user).order_by(Schedule.id) # ty: ignore[invalid-argument-type]
3636
)
3737
)
3838

penny/penny/database/history_store.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def get_latest(self, user: str, duration: str) -> ConversationHistory | None:
6060
ConversationHistory.user == user,
6161
ConversationHistory.duration == duration,
6262
)
63-
.order_by(ConversationHistory.period_start.desc()) # type: ignore[unresolved-attribute]
63+
.order_by(ConversationHistory.period_start.desc())
6464
.limit(1)
6565
).first()
6666

@@ -74,7 +74,7 @@ def get_recent(self, user: str, duration: str, limit: int = 14) -> list[Conversa
7474
ConversationHistory.user == user,
7575
ConversationHistory.duration == duration,
7676
)
77-
.order_by(ConversationHistory.period_start.desc()) # type: ignore[unresolved-attribute]
77+
.order_by(ConversationHistory.period_start.desc())
7878
.limit(limit)
7979
).all()
8080
)
@@ -133,7 +133,7 @@ def get_in_range(
133133
ConversationHistory.period_start >= start,
134134
ConversationHistory.period_start < end,
135135
)
136-
.order_by(ConversationHistory.period_start.asc()) # type: ignore[unresolved-attribute]
136+
.order_by(ConversationHistory.period_start.asc())
137137
).all()
138138
)
139139
return entries

penny/penny/database/message_store.py

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def find_outgoing_by_content(self, content: str) -> MessageLog | None:
185185
MessageLog.direction == PennyConstants.MessageDirection.OUTGOING,
186186
MessageLog.content.startswith(content),
187187
)
188-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
188+
.order_by(MessageLog.timestamp.desc())
189189
).first()
190190

191191
# --- Thread context ---
@@ -234,9 +234,7 @@ def _walk_thread(self, message_id: int, limit: int = 20) -> list[MessageLog]:
234234
def get_conversation_leaves(self) -> list[MessageLog]:
235235
"""Get outgoing leaf messages eligible for spontaneous continuation."""
236236
with self._session() as session:
237-
has_child = select(MessageLog.parent_id).where(
238-
MessageLog.parent_id.isnot(None) # type: ignore[unresolved-attribute]
239-
)
237+
has_child = select(MessageLog.parent_id).where(MessageLog.parent_id.isnot(None))
240238
incoming_ids = select(MessageLog.id).where(
241239
MessageLog.direction == PennyConstants.MessageDirection.INCOMING
242240
)
@@ -245,10 +243,10 @@ def get_conversation_leaves(self) -> list[MessageLog]:
245243
select(MessageLog)
246244
.where(
247245
MessageLog.direction == PennyConstants.MessageDirection.OUTGOING,
248-
MessageLog.id.notin_(has_child), # type: ignore[unresolved-attribute]
249-
MessageLog.parent_id.in_(incoming_ids), # type: ignore[unresolved-attribute]
246+
MessageLog.id.notin_(has_child),
247+
MessageLog.parent_id.in_(incoming_ids),
250248
)
251-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
249+
.order_by(MessageLog.timestamp.desc())
252250
).all()
253251
)
254252

@@ -262,7 +260,7 @@ def get_user_messages(self, sender: str, limit: int = 100) -> list[MessageLog]:
262260
MessageLog.sender == sender,
263261
MessageLog.direction == PennyConstants.MessageDirection.INCOMING,
264262
)
265-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
263+
.order_by(MessageLog.timestamp.desc())
266264
.limit(limit)
267265
).all()
268266
)
@@ -289,7 +287,7 @@ def _get_recent_incoming(self, session: Any, sender: str, limit: int) -> list[Me
289287
MessageLog.direction == PennyConstants.MessageDirection.INCOMING,
290288
MessageLog.is_reaction == False, # noqa: E712
291289
)
292-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
290+
.order_by(MessageLog.timestamp.desc())
293291
.limit(limit)
294292
).all()
295293
)
@@ -303,7 +301,7 @@ def _get_threaded_replies(self, session: Any, incoming: list[MessageLog]) -> lis
303301
session.exec(
304302
select(MessageLog).where(
305303
MessageLog.direction == PennyConstants.MessageDirection.OUTGOING,
306-
MessageLog.parent_id.in_(incoming_ids), # type: ignore[unresolved-attribute]
304+
MessageLog.parent_id.in_(incoming_ids),
307305
)
308306
).all()
309307
)
@@ -320,7 +318,7 @@ def _get_autonomous_outgoing(
320318
MessageLog.parent_id == None, # noqa: E711
321319
MessageLog.recipient == recipient,
322320
)
323-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
321+
.order_by(MessageLog.timestamp.desc())
324322
.limit(limit)
325323
).all()
326324
)
@@ -345,7 +343,7 @@ def get_messages_in_range(
345343
MessageLog.timestamp >= start,
346344
MessageLog.timestamp < end,
347345
)
348-
.order_by(MessageLog.timestamp) # type: ignore[unresolved-attribute]
346+
.order_by(MessageLog.timestamp) # ty: ignore[invalid-argument-type]
349347
).all()
350348
)
351349

@@ -364,7 +362,7 @@ def get_reactions_in_range(
364362
MessageLog.timestamp >= start,
365363
MessageLog.timestamp < end,
366364
)
367-
.order_by(MessageLog.timestamp) # type: ignore[unresolved-attribute]
365+
.order_by(MessageLog.timestamp) # ty: ignore[invalid-argument-type]
368366
).all()
369367
)
370368

@@ -382,7 +380,7 @@ def get_messages_since(
382380
MessageLog.is_reaction == False, # noqa: E712
383381
MessageLog.timestamp >= since,
384382
)
385-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
383+
.order_by(MessageLog.timestamp.desc())
386384
.limit(limit)
387385
).all()
388386
)
@@ -396,7 +394,7 @@ def get_messages_since(
396394
MessageLog.recipient == sender,
397395
MessageLog.timestamp >= since,
398396
)
399-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
397+
.order_by(MessageLog.timestamp.desc())
400398
.limit(limit)
401399
).all()
402400
)
@@ -416,7 +414,7 @@ def get_unprocessed(self, sender: str, limit: int) -> list[MessageLog]:
416414
MessageLog.is_reaction == False, # noqa: E712
417415
MessageLog.processed == False, # noqa: E712
418416
)
419-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
417+
.order_by(MessageLog.timestamp.desc())
420418
.limit(limit)
421419
).all()
422420
)
@@ -433,7 +431,7 @@ def get_user_reactions(self, sender: str, limit: int) -> list[MessageLog]:
433431
MessageLog.is_reaction == True, # noqa: E712
434432
MessageLog.processed == False, # noqa: E712
435433
)
436-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
434+
.order_by(MessageLog.timestamp.desc())
437435
.limit(limit)
438436
).all()
439437
)
@@ -468,11 +466,9 @@ def count_active_threads(self) -> int:
468466
with self._session() as session:
469467
from sqlalchemy import func
470468

471-
has_child = select(MessageLog.parent_id).where(
472-
MessageLog.parent_id.isnot(None) # type: ignore[unresolved-attribute]
473-
)
469+
has_child = select(MessageLog.parent_id).where(MessageLog.parent_id.isnot(None))
474470
return session.exec(
475-
select(func.count()).select_from(MessageLog).where(MessageLog.id.notin_(has_child)) # type: ignore[unresolved-attribute]
471+
select(func.count()).select_from(MessageLog).where(MessageLog.id.notin_(has_child))
476472
).one()
477473

478474
def get_latest_incoming_time(self, sender: str) -> datetime | None:
@@ -485,7 +481,7 @@ def get_latest_incoming_time(self, sender: str) -> datetime | None:
485481
MessageLog.direction == PennyConstants.MessageDirection.INCOMING,
486482
MessageLog.is_reaction == False, # noqa: E712
487483
)
488-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
484+
.order_by(MessageLog.timestamp.desc())
489485
.limit(1)
490486
).first()
491487

@@ -499,7 +495,7 @@ def get_latest_autonomous_outgoing_time(self, recipient: str) -> datetime | None
499495
MessageLog.parent_id == None, # noqa: E711
500496
MessageLog.recipient == recipient,
501497
)
502-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
498+
.order_by(MessageLog.timestamp.desc())
503499
.limit(1)
504500
).first()
505501

@@ -533,10 +529,10 @@ def get_last_checkin_time(self, prompt_text: str, hours: int = 48) -> datetime |
533529
return session.exec(
534530
select(PromptLog.timestamp)
535531
.where(
536-
PromptLog.messages.contains(prompt_text), # type: ignore[unresolved-attribute]
532+
PromptLog.messages.contains(prompt_text),
537533
PromptLog.timestamp >= cutoff,
538534
)
539-
.order_by(PromptLog.timestamp.desc()) # type: ignore[unresolved-attribute]
535+
.order_by(PromptLog.timestamp.desc())
540536
.limit(1)
541537
).first()
542538

@@ -553,7 +549,7 @@ def get_recent_outgoing_content(
553549
MessageLog.recipient == recipient,
554550
MessageLog.timestamp >= cutoff,
555551
)
556-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
552+
.order_by(MessageLog.timestamp.desc())
557553
.limit(limit)
558554
).all()
559555
return [m for m in messages if m]
@@ -577,7 +573,7 @@ def get_latest_message_time_in_range(
577573
MessageLog.timestamp >= start,
578574
MessageLog.timestamp < end,
579575
)
580-
.order_by(MessageLog.timestamp.desc()) # type: ignore[unresolved-attribute]
576+
.order_by(MessageLog.timestamp.desc())
581577
.limit(1)
582578
).first()
583579

@@ -591,6 +587,6 @@ def get_first_message_time(self, sender: str) -> datetime | None:
591587
MessageLog.direction == PennyConstants.MessageDirection.INCOMING,
592588
MessageLog.is_reaction == False, # noqa: E712
593589
)
594-
.order_by(MessageLog.timestamp) # type: ignore[unresolved-attribute]
590+
.order_by(MessageLog.timestamp) # ty: ignore[invalid-argument-type]
595591
.limit(1)
596592
).first()

0 commit comments

Comments
 (0)