Compare commits
1 Commits
main
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c58093dccc |
@@ -18,9 +18,17 @@ jobs:
|
|||||||
- name: Lint (ruff via tox)
|
- name: Lint (ruff via tox)
|
||||||
run: tox -e lint
|
run: tox -e lint
|
||||||
|
|
||||||
test:
|
typecheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: lint
|
needs: lint
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Type-check (mypy via tox)
|
||||||
|
run: tox -e typecheck
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: typecheck
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Run tests (via tox)
|
- name: Run tests (via tox)
|
||||||
|
|||||||
@@ -164,3 +164,7 @@ directory = "htmlcov"
|
|||||||
|
|
||||||
[tool.coverage.xml]
|
[tool.coverage.xml]
|
||||||
output = "coverage.xml"
|
output = "coverage.xml"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
ignore_missing_imports = true
|
||||||
|
no_error_summary = true
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import sqlite3
|
|||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
|
|
||||||
@@ -36,9 +38,9 @@ def _discover_databases() -> list[dict]:
|
|||||||
return dbs
|
return dbs
|
||||||
|
|
||||||
|
|
||||||
def _query_database(db_path: str) -> dict:
|
def _query_database(db_path: str) -> dict[str, Any]:
|
||||||
"""Open a database read-only and return all tables with their rows."""
|
"""Open a database read-only and return all tables with their rows."""
|
||||||
result = {"tables": {}, "error": None}
|
result: dict[str, Any] = {"tables": {}, "error": None}
|
||||||
try:
|
try:
|
||||||
with closing(sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)) as conn:
|
with closing(sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)) as conn:
|
||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class HermesMonitor:
|
|||||||
message=f"Check error: {r}",
|
message=f"Check error: {r}",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
elif isinstance(r, CheckResult):
|
||||||
checks.append(r)
|
checks.append(r)
|
||||||
|
|
||||||
# Compute overall level
|
# Compute overall level
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ async def reload_config(
|
|||||||
@router.get("/history")
|
@router.get("/history")
|
||||||
async def get_history(
|
async def get_history(
|
||||||
hours: int = 24,
|
hours: int = 24,
|
||||||
store: Annotated[HealthHistoryStore, Depends(get_history_store)] = None,
|
store: Annotated[HealthHistoryStore | None, Depends(get_history_store)] = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
"""Get provider health history for the last N hours."""
|
"""Get provider health history for the last N hours."""
|
||||||
if store is None:
|
if store is None:
|
||||||
|
|||||||
@@ -744,19 +744,20 @@ class CascadeRouter:
|
|||||||
self,
|
self,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
messages: list[dict],
|
messages: list[dict],
|
||||||
model: str,
|
model: str | None,
|
||||||
temperature: float,
|
temperature: float,
|
||||||
max_tokens: int | None,
|
max_tokens: int | None,
|
||||||
content_type: ContentType = ContentType.TEXT,
|
content_type: ContentType = ContentType.TEXT,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Try a single provider request."""
|
"""Try a single provider request."""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
effective_model: str = model or provider.get_default_model() or ""
|
||||||
|
|
||||||
if provider.type == "ollama":
|
if provider.type == "ollama":
|
||||||
result = await self._call_ollama(
|
result = await self._call_ollama(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model=model or provider.get_default_model(),
|
model=effective_model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
@@ -765,7 +766,7 @@ class CascadeRouter:
|
|||||||
result = await self._call_openai(
|
result = await self._call_openai(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model=model or provider.get_default_model(),
|
model=effective_model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
)
|
)
|
||||||
@@ -773,7 +774,7 @@ class CascadeRouter:
|
|||||||
result = await self._call_anthropic(
|
result = await self._call_anthropic(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model=model or provider.get_default_model(),
|
model=effective_model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
)
|
)
|
||||||
@@ -781,7 +782,7 @@ class CascadeRouter:
|
|||||||
result = await self._call_grok(
|
result = await self._call_grok(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model=model or provider.get_default_model(),
|
model=effective_model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
)
|
)
|
||||||
@@ -789,7 +790,7 @@ class CascadeRouter:
|
|||||||
result = await self._call_vllm_mlx(
|
result = await self._call_vllm_mlx(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model=model or provider.get_default_model(),
|
model=effective_model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
)
|
)
|
||||||
|
|||||||
20
src/integrations/chat_bridge/vendors/discord.py
vendored
20
src/integrations/chat_bridge/vendors/discord.py
vendored
@@ -474,7 +474,7 @@ class DiscordVendor(ChatPlatform):
|
|||||||
async def _run_client(self, token: str) -> None:
|
async def _run_client(self, token: str) -> None:
|
||||||
"""Run the discord.py client (blocking call in a task)."""
|
"""Run the discord.py client (blocking call in a task)."""
|
||||||
try:
|
try:
|
||||||
await self._client.start(token)
|
await self._client.start(token) # type: ignore[union-attr]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Discord client error: %s", exc)
|
logger.error("Discord client error: %s", exc)
|
||||||
self._state = PlatformState.ERROR
|
self._state = PlatformState.ERROR
|
||||||
@@ -482,32 +482,32 @@ class DiscordVendor(ChatPlatform):
|
|||||||
def _register_handlers(self) -> None:
|
def _register_handlers(self) -> None:
|
||||||
"""Register Discord event handlers on the client."""
|
"""Register Discord event handlers on the client."""
|
||||||
|
|
||||||
@self._client.event
|
@self._client.event # type: ignore[union-attr]
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
self._guild_count = len(self._client.guilds)
|
self._guild_count = len(self._client.guilds) # type: ignore[union-attr]
|
||||||
self._state = PlatformState.CONNECTED
|
self._state = PlatformState.CONNECTED
|
||||||
logger.info(
|
logger.info(
|
||||||
"Discord ready: %s in %d guild(s)",
|
"Discord ready: %s in %d guild(s)",
|
||||||
self._client.user,
|
self._client.user, # type: ignore[union-attr]
|
||||||
self._guild_count,
|
self._guild_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
@self._client.event
|
@self._client.event # type: ignore[union-attr]
|
||||||
async def on_message(message):
|
async def on_message(message):
|
||||||
# Ignore our own messages
|
# Ignore our own messages
|
||||||
if message.author == self._client.user:
|
if message.author == self._client.user: # type: ignore[union-attr]
|
||||||
return
|
return
|
||||||
|
|
||||||
# Only respond to mentions or DMs
|
# Only respond to mentions or DMs
|
||||||
is_dm = not hasattr(message.channel, "guild") or message.channel.guild is None
|
is_dm = not hasattr(message.channel, "guild") or message.channel.guild is None
|
||||||
is_mention = self._client.user in message.mentions
|
is_mention = self._client.user in message.mentions # type: ignore[union-attr]
|
||||||
|
|
||||||
if not is_dm and not is_mention:
|
if not is_dm and not is_mention:
|
||||||
return
|
return
|
||||||
|
|
||||||
await self._handle_message(message)
|
await self._handle_message(message)
|
||||||
|
|
||||||
@self._client.event
|
@self._client.event # type: ignore[union-attr]
|
||||||
async def on_disconnect():
|
async def on_disconnect():
|
||||||
if self._state != PlatformState.DISCONNECTED:
|
if self._state != PlatformState.DISCONNECTED:
|
||||||
self._state = PlatformState.CONNECTING
|
self._state = PlatformState.CONNECTING
|
||||||
@@ -535,8 +535,8 @@ class DiscordVendor(ChatPlatform):
|
|||||||
def _extract_content(self, message) -> str:
|
def _extract_content(self, message) -> str:
|
||||||
"""Strip the bot mention and return clean message text."""
|
"""Strip the bot mention and return clean message text."""
|
||||||
content = message.content
|
content = message.content
|
||||||
if self._client.user:
|
if self._client.user: # type: ignore[union-attr]
|
||||||
content = content.replace(f"<@{self._client.user.id}>", "").strip()
|
content = content.replace(f"<@{self._client.user.id}>", "").strip() # type: ignore[union-attr]
|
||||||
return content
|
return content
|
||||||
|
|
||||||
async def _invoke_agent(self, content: str, session_id: str, target):
|
async def _invoke_agent(self, content: str, session_id: str, target):
|
||||||
|
|||||||
@@ -102,14 +102,14 @@ class TelegramBot:
|
|||||||
self._token = tok
|
self._token = tok
|
||||||
self._app = Application.builder().token(tok).build()
|
self._app = Application.builder().token(tok).build()
|
||||||
|
|
||||||
self._app.add_handler(CommandHandler("start", self._cmd_start))
|
self._app.add_handler(CommandHandler("start", self._cmd_start)) # type: ignore[union-attr]
|
||||||
self._app.add_handler(
|
self._app.add_handler( # type: ignore[union-attr]
|
||||||
MessageHandler(filters.TEXT & ~filters.COMMAND, self._handle_message)
|
MessageHandler(filters.TEXT & ~filters.COMMAND, self._handle_message)
|
||||||
)
|
)
|
||||||
|
|
||||||
await self._app.initialize()
|
await self._app.initialize() # type: ignore[union-attr]
|
||||||
await self._app.start()
|
await self._app.start() # type: ignore[union-attr]
|
||||||
await self._app.updater.start_polling(allowed_updates=Update.ALL_TYPES)
|
await self._app.updater.start_polling(allowed_updates=Update.ALL_TYPES) # type: ignore[union-attr]
|
||||||
|
|
||||||
self._running = True
|
self._running = True
|
||||||
logger.info("Telegram bot started.")
|
logger.info("Telegram bot started.")
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ class VoiceLoop:
|
|||||||
def _transcribe(self, audio: np.ndarray) -> str:
|
def _transcribe(self, audio: np.ndarray) -> str:
|
||||||
"""Transcribe audio using local Whisper model."""
|
"""Transcribe audio using local Whisper model."""
|
||||||
self._load_whisper()
|
self._load_whisper()
|
||||||
|
assert self._whisper_model is not None, "Whisper model failed to load"
|
||||||
|
|
||||||
sys.stdout.write(" 🧠 Transcribing...\r")
|
sys.stdout.write(" 🧠 Transcribing...\r")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|||||||
10
tox.ini
10
tox.ini
@@ -41,8 +41,10 @@ description = Static type checking with mypy
|
|||||||
commands_pre =
|
commands_pre =
|
||||||
deps =
|
deps =
|
||||||
mypy>=1.0.0
|
mypy>=1.0.0
|
||||||
|
types-PyYAML
|
||||||
|
types-requests
|
||||||
commands =
|
commands =
|
||||||
mypy src --ignore-missing-imports --no-error-summary
|
mypy src
|
||||||
|
|
||||||
# ── Test Environments ────────────────────────────────────────────────────────
|
# ── Test Environments ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -130,13 +132,17 @@ commands =
|
|||||||
# ── Pre-push (mirrors CI exactly) ────────────────────────────────────────────
|
# ── Pre-push (mirrors CI exactly) ────────────────────────────────────────────
|
||||||
|
|
||||||
[testenv:pre-push]
|
[testenv:pre-push]
|
||||||
description = Local gate — lint + full CI suite (same as Gitea Actions)
|
description = Local gate — lint + typecheck + full CI suite (same as Gitea Actions)
|
||||||
deps =
|
deps =
|
||||||
ruff>=0.8.0
|
ruff>=0.8.0
|
||||||
|
mypy>=1.0.0
|
||||||
|
types-PyYAML
|
||||||
|
types-requests
|
||||||
commands =
|
commands =
|
||||||
ruff check src/ tests/
|
ruff check src/ tests/
|
||||||
ruff format --check src/ tests/
|
ruff format --check src/ tests/
|
||||||
bash -c 'files=$(grep -rl "<style" src/dashboard/templates/ --include="*.html" 2>/dev/null); if [ -n "$files" ]; then echo "ERROR: inline <style> blocks found — move CSS to static/css/mission-control.css:"; echo "$files"; exit 1; fi; echo "No inline CSS — OK"'
|
bash -c 'files=$(grep -rl "<style" src/dashboard/templates/ --include="*.html" 2>/dev/null); if [ -n "$files" ]; then echo "ERROR: inline <style> blocks found — move CSS to static/css/mission-control.css:"; echo "$files"; exit 1; fi; echo "No inline CSS — OK"'
|
||||||
|
mypy src
|
||||||
mkdir -p reports
|
mkdir -p reports
|
||||||
pytest tests/ \
|
pytest tests/ \
|
||||||
--cov=src \
|
--cov=src \
|
||||||
|
|||||||
Reference in New Issue
Block a user