Skip to content

Commit 19a9aa7

Browse files
committed
fix bugs
1 parent dd6af6f commit 19a9aa7

15 files changed

Lines changed: 60 additions & 50 deletions

File tree

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ run-prod: ## Run the production server
3333

3434
# ─── Quality ─────────────────────────────────────────────
3535

36-
test: ## Run all tests
37-
poetry run pytest tests/ -v
36+
test: ## Run all tests (excludes adapter integration tests that require Docker)
37+
poetry run pytest tests/ -v -m "not (elasticsearch or opensearch or solr or meilisearch)"
3838

3939
test-unit: ## Run unit tests only
4040
poetry run pytest tests/unit/ -v -m "not integration"

src/opensift/adapters/meilisearch/adapter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import logging
2222
import time
2323
from datetime import UTC, datetime
24-
from typing import Any
24+
from typing import Any, cast
2525

2626
import httpx
2727

@@ -171,7 +171,7 @@ async def fetch_document(self, doc_id: str) -> dict[str, Any]:
171171
if resp.status_code == 404:
172172
raise DocumentNotFoundError(f"Document '{doc_id}' not found.")
173173
resp.raise_for_status()
174-
return resp.json()
174+
return cast(dict[str, Any], resp.json())
175175
except DocumentNotFoundError:
176176
raise
177177
except httpx.HTTPError as e:

src/opensift/adapters/wikipedia/adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async def initialize(self) -> None:
7878
ConfigurationError: If the ``wikipedia-api`` package is not installed.
7979
"""
8080
try:
81-
import wikipediaapi
81+
import wikipediaapi # type: ignore[import-untyped]
8282
except ImportError as e:
8383
raise ConfigurationError(
8484
"The 'wikipedia-api' package is required for the Wikipedia adapter. "

src/opensift/api/v1/endpoints/search.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import json
1414
import logging
15+
from collections.abc import AsyncIterator
1516

1617
from fastapi import APIRouter, Depends, HTTPException
1718
from fastapi.responses import StreamingResponse
@@ -103,7 +104,7 @@ async def search(
103104
) from e
104105

105106

106-
async def _sse_generator(engine: OpenSiftEngine, request: SearchRequest):
107+
async def _sse_generator(engine: OpenSiftEngine, request: SearchRequest) -> AsyncIterator[str]:
107108
"""Async generator that yields SSE-formatted lines.
108109
109110
Each event follows the SSE protocol::

src/opensift/client/client.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
import asyncio
1717
import json
1818
import logging
19-
from collections.abc import AsyncIterator, Iterator
20-
from typing import Any
19+
from collections.abc import AsyncIterator, Coroutine, Iterator
20+
from typing import Any, TypeVar, cast
2121

2222
import httpx
2323

24+
_T = TypeVar("_T")
25+
2426
logger = logging.getLogger(__name__)
2527

2628
# ═══════════════════════════════════════════════════════════════════════════════
@@ -95,7 +97,7 @@ async def health(self) -> dict[str, Any]:
9597
"""
9698
resp = await self._client.get("/v1/health")
9799
resp.raise_for_status()
98-
return resp.json()
100+
return cast(dict[str, Any], resp.json())
99101

100102
async def adapter_health(self) -> dict[str, Any]:
101103
"""Check adapter health.
@@ -105,7 +107,7 @@ async def adapter_health(self) -> dict[str, Any]:
105107
"""
106108
resp = await self._client.get("/v1/health/adapters")
107109
resp.raise_for_status()
108-
return resp.json()
110+
return cast(dict[str, Any], resp.json())
109111

110112
# ── Plan (standalone) ──
111113

@@ -136,7 +138,7 @@ async def plan(
136138
}
137139
resp = await self._client.post("/v1/plan", json=payload)
138140
resp.raise_for_status()
139-
return resp.json()
141+
return cast(dict[str, Any], resp.json())
140142

141143
# ── Search (complete mode) ──
142144

@@ -180,7 +182,7 @@ async def search(
180182
}
181183
resp = await self._client.post("/v1/search", json=payload)
182184
resp.raise_for_status()
183-
return resp.json()
185+
return cast(dict[str, Any], resp.json())
184186

185187
# ── Search (streaming mode) ──
186188

@@ -270,7 +272,7 @@ async def batch_search(
270272

271273
resp = await self._client.post("/v1/search/batch", json=payload)
272274
resp.raise_for_status()
273-
return resp.json()
275+
return cast(dict[str, Any], resp.json())
274276

275277

276278
# ═══════════════════════════════════════════════════════════════════════════════
@@ -306,7 +308,7 @@ def __init__(
306308
self._timeout = timeout
307309
self._httpx_kwargs = httpx_kwargs
308310

309-
def _run(self, coro):
311+
def _run(self, coro: Coroutine[Any, Any, _T]) -> _T:
310312
"""Run an async coroutine synchronously."""
311313
try:
312314
loop = asyncio.get_running_loop()

src/opensift/config/settings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ def _parse_hosts(cls, v: Any) -> list[str]:
7474
try:
7575
parsed = json.loads(v)
7676
if isinstance(parsed, list):
77-
return parsed
77+
return [str(h) for h in parsed]
7878
except (json.JSONDecodeError, TypeError):
7979
pass
8080
# Single host as plain string
8181
return [v] if v else []
82-
return v
82+
return list(v)
8383

8484

8585
class SearchSettings(BaseModel):
@@ -141,7 +141,7 @@ def from_yaml(cls, path: str | Path) -> Settings:
141141
Returns:
142142
Populated Settings instance.
143143
"""
144-
import yaml
144+
import yaml # type: ignore[import-untyped]
145145

146146
config_path = Path(path)
147147
if not config_path.exists():

src/opensift/core/classifier.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from opensift.models.assessment import (
2020
AssessmentType,
21+
CriterionAssessment,
2122
ResultClassification,
2223
ScoredResult,
2324
ValidationResult,
@@ -105,7 +106,7 @@ def classify_batch(
105106
return results
106107

107108
@staticmethod
108-
def _classify_single(assessments: list) -> ResultClassification:
109+
def _classify_single(assessments: list[CriterionAssessment]) -> ResultClassification:
109110
"""Classification for a single-criterion scenario.
110111
111112
support → perfect
@@ -125,7 +126,9 @@ def _classify_single(assessments: list) -> ResultClassification:
125126
return ResultClassification.REJECT
126127

127128
@staticmethod
128-
def _classify_multiple(assessments: list, criteria_map: dict) -> ResultClassification:
129+
def _classify_multiple(
130+
assessments: list[CriterionAssessment], criteria_map: dict[str, Criterion]
131+
) -> ResultClassification:
129132
"""Classification for multi-criteria scenario.
130133
131134
ALL support → perfect
@@ -151,7 +154,7 @@ def _classify_multiple(assessments: list, criteria_map: dict) -> ResultClassific
151154
return ResultClassification.REJECT
152155

153156
@staticmethod
154-
def _calculate_weighted_score(assessments: list, criteria_map: dict) -> float:
157+
def _calculate_weighted_score(assessments: list[CriterionAssessment], criteria_map: dict[str, Criterion]) -> float:
155158
"""Calculate weighted score based on assessments and criteria weights.
156159
157160
Score mapping:

src/opensift/core/engine.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import time
2121
import uuid
2222
from collections.abc import AsyncIterator
23-
from typing import TYPE_CHECKING
23+
from typing import TYPE_CHECKING, Any
2424

2525
from opensift.adapters.base.registry import AdapterRegistry
2626
from opensift.core.classifier import ResultClassifier
@@ -444,13 +444,12 @@ async def batch_search(self, request: BatchSearchRequest) -> BatchSearchResponse
444444
for query in request.queries
445445
]
446446

447-
responses = await asyncio.gather(*tasks, return_exceptions=True)
447+
raw_responses: list[SearchResponse | BaseException] = await asyncio.gather(*tasks, return_exceptions=True)
448448

449449
results: list[SearchResponse] = []
450-
for i, resp in enumerate(responses):
451-
if isinstance(resp, Exception):
450+
for i, resp in enumerate(raw_responses):
451+
if isinstance(resp, BaseException):
452452
logger.warning("Batch query %d failed: %s", i, resp)
453-
# Create an error response stub
454453
results.append(
455454
SearchResponse(
456455
request_id=f"req_batch_{i}_error",
@@ -584,25 +583,29 @@ async def _execute_searches(
584583
logger.warning("No search adapter available, returning empty results")
585584
return []
586585

587-
tasks: list[asyncio.Task] = []
586+
tasks: list[asyncio.Task[Any]] = []
588587
task_meta: list[tuple[str, bool]] = [] # (adapter_name, use_paper_path)
589588

590589
for adapter in adapters:
591590
use_paper_path = hasattr(adapter, "search_papers") and callable(adapter.search_papers)
592591
for query in search_queries:
593592
if use_paper_path:
594-
tasks.append(adapter.search_papers(query, request.options)) # type: ignore[union-attr]
593+
tasks.append(
594+
asyncio.ensure_future(
595+
adapter.search_papers(query, request.options) # type: ignore[attr-defined]
596+
)
597+
)
595598
else:
596-
tasks.append(adapter.search_and_normalize(query, request.options))
599+
tasks.append(asyncio.ensure_future(adapter.search_and_normalize(query, request.options)))
597600
task_meta.append((adapter.name, use_paper_path))
598601

599-
raw_results = await asyncio.gather(*tasks, return_exceptions=True)
602+
raw_results: list[Any] = await asyncio.gather(*tasks, return_exceptions=True)
600603

601604
seen_titles: set[str] = set()
602605
items: list[ResultItem] = []
603606

604607
for i, result in enumerate(raw_results):
605-
if isinstance(result, Exception):
608+
if isinstance(result, BaseException):
606609
adapter_name, _ = task_meta[i]
607610
logger.warning("Search query failed on adapter '%s': %s", adapter_name, result)
608611
continue

src/opensift/core/llm/client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import json
1010
import logging
1111
import re
12-
from typing import TYPE_CHECKING, Any
12+
from typing import TYPE_CHECKING, Any, cast
1313

1414
from openai import AsyncOpenAI
1515

@@ -199,7 +199,7 @@ async def chat_json(
199199
content = self._strip_code_fences(content)
200200

201201
try:
202-
return json.loads(content)
202+
return cast(dict[str, Any], json.loads(content))
203203
except json.JSONDecodeError:
204204
repaired = self._repair_json(content)
205205
if repaired is not None:
@@ -369,7 +369,7 @@ def _repair_json(text: str) -> dict[str, Any] | None:
369369

370370
# Try parsing after basic fixes
371371
try:
372-
return json.loads(text)
372+
return cast(dict[str, Any], json.loads(text))
373373
except json.JSONDecodeError:
374374
pass
375375

@@ -381,7 +381,7 @@ def _repair_json(text: str) -> dict[str, Any] | None:
381381
text = re.sub(r"(})\s*(\")", r"\1,\2", text)
382382

383383
try:
384-
return json.loads(text)
384+
return cast(dict[str, Any], json.loads(text))
385385
except json.JSONDecodeError:
386386
pass
387387

@@ -410,7 +410,7 @@ def _escape_newlines_in_strings(s: str) -> str:
410410
text = _escape_newlines_in_strings(text)
411411

412412
try:
413-
return json.loads(text)
413+
return cast(dict[str, Any], json.loads(text))
414414
except json.JSONDecodeError:
415415
return None
416416

src/opensift/core/planner/planner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import logging
1414
import time
15-
from typing import TYPE_CHECKING
15+
from typing import TYPE_CHECKING, Any
1616

1717
from opensift.core.llm.client import LLMClient, LLMError
1818
from opensift.core.llm.prompts import CRITERIA_SYSTEM_PROMPT, CRITERIA_USER_PROMPT
@@ -122,7 +122,7 @@ async def _generate_with_llm(self, query: str) -> CriteriaResult:
122122

123123
return self._parse_criteria_response(raw)
124124

125-
def _parse_criteria_response(self, raw: dict) -> CriteriaResult:
125+
def _parse_criteria_response(self, raw: dict[str, Any]) -> CriteriaResult:
126126
"""Parse and validate the raw LLM response into a CriteriaResult.
127127
128128
Args:

0 commit comments

Comments
 (0)