Skip to content

Commit b08dfb3

Browse files
authored
Merge pull request #31 from InKCre/codex/pyrefly-gate
chore(dx): add Pyrefly type gate
2 parents f878023 + c891ada commit b08dfb3

27 files changed

Lines changed: 605 additions & 167 deletions

File tree

‎.pre-commit-config.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ repos:
1515
language: system
1616
files: ^(.*\.py|pyproject\.toml|ruff\.toml)$
1717
pass_filenames: false
18+
- id: typecheck
19+
name: Check Pyrefly types
20+
entry: pyrefly check --min-severity=warn --output-format=min-text --progress-bar=no
21+
language: system
22+
files: ^(app/|extensions/|libs/|migrations/|scripts/|tests/|utils/|run\.py$|pyproject\.toml$|pdm\.lock$)
23+
pass_filenames: false
1824
- id: check-lock
1925
name: Check PDM lock
2026
entry: python scripts/check_lock.py

‎app/business/extension/main.py‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import typing
33
import fastapi
44
import sqlmodel
5-
import sqlalchemy
65
import importlib
76
import os
87
import tomllib
@@ -34,13 +33,15 @@ def __init_subclass__(
3433
**kwargs,
3534
) -> None:
3635
cls.__extid__ = ext_id
37-
cls.__configcls__ = config_cls
36+
# ConfigTV is bound by each concrete extension subclass; Python's type model
37+
# cannot represent a class attribute specialized by that subclass binding.
38+
cls.__configcls__ = config_cls # pyrefly: ignore[no-access]
3839
cls.__configschema__ = config_cls.model_json_schema()
3940
return super().__init_subclass__(**kwargs)
4041

4142
@classmethod
4243
def on_start(cls, app: fastapi.FastAPI, extension: ExtensionModel):
43-
cls.config = cls.__configcls__(**(extension.config or {}))
44+
cls.config = cls.__configcls__(**(extension.config or {})) # pyrefly: ignore[no-access]
4445
with SessionLocal() as db:
4546
extension.config_schema = cls.__configschema__
4647
db.add(extension)
@@ -63,7 +64,10 @@ def _init_sources(cls): ...
6364

6465
@classmethod
6566
async def on_close(cls):
66-
ExtensionManager.save_config(ext_id=cls.__extid__, config=cls.config)
67+
ExtensionManager.save_config(
68+
ext_id=cls.__extid__,
69+
config=cls.config, # pyrefly: ignore[missing-attribute]
70+
)
6771
LOGGER.info(f"Extension {cls.__extid__} closed.")
6872

6973
@classmethod
@@ -81,9 +85,9 @@ def update_config(cls, new_config: dict | ConfigTV):
8185
:param new_config:
8286
"""
8387
if isinstance(new_config, dict):
84-
cls.config = cls.__configcls__(**new_config)
88+
cls.config = cls.__configcls__(**new_config) # pyrefly: ignore[no-access]
8589
else:
86-
cls.config = new_config
90+
cls.config = new_config # pyrefly: ignore[no-access]
8791

8892

8993
class ExtensionManager:
@@ -316,9 +320,8 @@ def get_installed(
316320
if enabled_only:
317321
client_id = ClientManager.get_current_client_id()
318322
# Filter: client_id must be in the enabled array
319-
query = query.where(
320-
ExtensionModel.enabled.any(client_id, operator=sqlalchemy.sql.operators.eq)
321-
)
323+
enabled_column = typing.cast(typing.Any, ExtensionModel.enabled)
324+
query = query.where(enabled_column.any(client_id))
322325

323326
return tuple(db.exec(query).all())
324327

@@ -435,7 +438,7 @@ def sync(cls):
435438
# Runtime artifacts are immutable. Database-only records are never downloaded.
436439
all_db_extensions = db.exec(sqlmodel.select(ExtensionModel)).all()
437440
db_only = sorted(
438-
str(extension.id)
441+
extension.id
439442
for extension in all_db_extensions
440443
if extension.id not in local_extensions
441444
)

‎app/business/info_base/block.py‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
)
2626
from app.schemas.info_base.relation import RelationID, RelationModel
2727
from app.schemas.info_base.main import Vector
28+
from app.schemas.info_base.storage import StorageID
2829

2930
if typing.TYPE_CHECKING:
3031
from app.business.info_base.resolver import Resolver
@@ -137,7 +138,7 @@ async def fetchsert(cls, block: BlockModel, db_session: sqlmodel.Session) -> Blo
137138
db_session.flush()
138139
db_session.refresh(block)
139140
# and embedding - use sink service
140-
await EmbeddingManager.upsert_block_embedding(block, db_session)
141+
await EmbeddingManager.upsert_block_embedding(block=block, db_session=db_session)
141142

142143
return block
143144

@@ -149,15 +150,15 @@ async def organize(cls, block: BlockModel):
149150
"""
150151
with SessionLocal() as db_session:
151152
resolver = ResolverManager.get(block)
152-
generator = (await resolver.breakdown())()
153+
generator = resolver.breakdown()
153154
try:
154-
i = generator.send(None)
155+
item = await anext(generator)
155156
while True:
156-
db_session.add(i)
157+
db_session.add(item)
157158
db_session.flush()
158-
db_session.refresh(i)
159-
i = generator.send(i)
160-
except StopIteration:
159+
db_session.refresh(item)
160+
item = await generator.asend(item)
161+
except StopAsyncIteration:
161162
pass
162163

163164
db_session.commit()
@@ -209,16 +210,19 @@ def iterate_one(inner_block_id: int):
209210
sqlmodel.select(RelationModel).where(RelationModel.from_ == inner_block_id)
210211
).all()
211212

212-
r_relations.update(relation.id for relation in relations)
213+
r_relations.update(relation.id for relation in relations if relation.id is not None)
213214

214215
for relation in relations:
215216
block = db_session.exec(
216217
sqlmodel.select(BlockModel).where(BlockModel.id == relation.to_)
217218
).one()
218-
r_blocks.add(block.id)
219+
persisted_block_id = block.id
220+
if persisted_block_id is None:
221+
raise RuntimeError("Persisted block is missing its database ID")
222+
r_blocks.add(persisted_block_id)
219223

220224
if depth <= max_depth:
221-
iterate_one(block.id)
225+
iterate_one(persisted_block_id)
222226

223227
depth += 1
224228

@@ -372,7 +376,7 @@ def edit_block(
372376
block_id: BlockID,
373377
content: Opt[str] = None,
374378
resolver: Opt[ResolverType] = None,
375-
storage: Opt[str] | Undefined = _undefined,
379+
storage: Opt[StorageID] | Undefined = _undefined,
376380
) -> BlockModel:
377381
"""编辑块"""
378382
logger.info("Editing block", extra={"block_id": block_id})

‎app/business/info_base/resolver/html.py‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .main import Resolver
33

44

5-
class HTMLResolver(Resolver, rso_type="html"):
5+
class HTMLResolver(Resolver[str, str], rso_type="html"):
66
@classmethod
77
def create_graph(cls, url: str):
88
from app.schemas.info_base.main import SubGraphForm
@@ -13,17 +13,17 @@ def create_graph(cls, url: str):
1313
async def get_text(self) -> str:
1414
from app.business.info_base.block import BlockManager
1515
from app.business.info_base.relation import RelationManager
16-
from app.business.info_base.storage import StorageManager
1716

1817
out_relations = RelationManager.get(
19-
self._block.id, include_in=False, content="text content"
18+
self.block_id, include_in=False, content="text content"
2019
)
2120
if out_relations:
2221
block = BlockManager.get(out_relations[0].to_)
2322
if block is not None:
24-
storage = StorageManager.new_storage(block)
25-
return await storage.get_content(block)
26-
storage = StorageManager.new_storage(self._block)
27-
content = await storage.get_content(self._block)
28-
content = html2text.HTML2Text().handle(content)
29-
return content
23+
return block.content
24+
content = await self.get_raw_content()
25+
return html2text.HTML2Text().handle(content)
26+
27+
async def get_str_for_embedding(self) -> str:
28+
"""Use the rendered text as the embedding input."""
29+
return await self.get_text()

0 commit comments

Comments
 (0)