fix: resolve all CI lint and security issues
- ruff: 602→0 errors (428 auto-fixed, pyproject.toml ignores for FastAPI patterns) - bandit: skip B310 (urllib for vault file access is intentional) - Fixed SIM118 (dict.keys()→dict), PERF102, SIM113, SIM117 - Created pyproject.toml with ruff + bandit config - 285 tests still pass
This commit is contained in:
+23
-22
@@ -4,12 +4,13 @@ Supports: DeepSeek, OpenRouter, Google Gemini.
|
||||
Configured via environment variables.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
import httpx
|
||||
from typing import Optional, Literal
|
||||
|
||||
logger = logging.getLogger("obsigate.ai")
|
||||
|
||||
@@ -29,7 +30,7 @@ def _read_ai_keys() -> dict:
|
||||
def get_ai_key(env_name: str) -> str:
|
||||
"""Get AI key: stored file first, then .env fallback."""
|
||||
keys = _read_ai_keys()
|
||||
if env_name in keys and keys[env_name]:
|
||||
if keys.get(env_name):
|
||||
return keys[env_name]
|
||||
return os.getenv(env_name, "").strip()
|
||||
|
||||
@@ -67,7 +68,7 @@ PROVIDERS = _load_provider_keys()
|
||||
DEFAULT_PROVIDER: ProviderName = os.getenv("AI_DEFAULT_PROVIDER", "deepseek") # type: ignore
|
||||
|
||||
|
||||
def _get_provider_config(provider: Optional[ProviderName] = None) -> dict:
|
||||
def _get_provider_config(provider: ProviderName | None = None) -> dict:
|
||||
"""Get provider config, falling back to default if requested provider unavailable."""
|
||||
p = provider or DEFAULT_PROVIDER
|
||||
if p not in PROVIDERS:
|
||||
@@ -83,7 +84,7 @@ def _get_provider_config(provider: Optional[ProviderName] = None) -> dict:
|
||||
return {"name": p, **cfg}
|
||||
|
||||
|
||||
async def _call_deepseek_openrouter(prompt: str, system: str, provider: Optional[ProviderName] = None,
|
||||
async def _call_deepseek_openrouter(prompt: str, system: str, provider: ProviderName | None = None,
|
||||
temperature: float = 0.7, max_tokens: int = 2048) -> str:
|
||||
"""Call OpenAI-compatible API (DeepSeek, OpenRouter)."""
|
||||
cfg = _get_provider_config(provider)
|
||||
@@ -133,7 +134,7 @@ async def _call_gemini(prompt: str, system: str, temperature: float = 0.7, max_t
|
||||
return data["candidates"][0]["content"]["parts"][0]["text"].strip()
|
||||
|
||||
|
||||
async def ai_complete(prompt: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_complete(prompt: str, provider: ProviderName | None = None) -> str:
|
||||
"""Generic AI completion. Routes to appropriate provider."""
|
||||
cfg = _get_provider_config(provider)
|
||||
if cfg["name"] == "gemini":
|
||||
@@ -148,7 +149,7 @@ Your responses should be direct and concise. When editing text, return ONLY the
|
||||
no explanations or markdown fences."""
|
||||
|
||||
|
||||
async def ai_improve_writing(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_improve_writing(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Improve writing quality while preserving meaning."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Improve the following text. Fix grammar, clarity, and flow. Preserve the original language and meaning.\n\n{text}",
|
||||
@@ -156,7 +157,7 @@ async def ai_improve_writing(text: str, provider: Optional[ProviderName] = None)
|
||||
)
|
||||
|
||||
|
||||
async def ai_fix_spelling(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_fix_spelling(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Fix spelling and grammar errors."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Fix all spelling and grammar errors in this text. Return only the corrected text.\n\n{text}",
|
||||
@@ -164,7 +165,7 @@ async def ai_fix_spelling(text: str, provider: Optional[ProviderName] = None) ->
|
||||
)
|
||||
|
||||
|
||||
async def ai_make_shorter(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_make_shorter(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Make text more concise."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Make this text shorter and more concise while preserving the key information.\n\n{text}",
|
||||
@@ -172,7 +173,7 @@ async def ai_make_shorter(text: str, provider: Optional[ProviderName] = None) ->
|
||||
)
|
||||
|
||||
|
||||
async def ai_make_longer(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_make_longer(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Expand text with more detail."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Expand this text with more detail, examples, or explanation while keeping the same tone.\n\n{text}",
|
||||
@@ -180,7 +181,7 @@ async def ai_make_longer(text: str, provider: Optional[ProviderName] = None) ->
|
||||
)
|
||||
|
||||
|
||||
async def ai_simplify(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_simplify(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Simplify language."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Simplify this text. Use clearer, more straightforward language. Avoid jargon.\n\n{text}",
|
||||
@@ -188,7 +189,7 @@ async def ai_simplify(text: str, provider: Optional[ProviderName] = None) -> str
|
||||
)
|
||||
|
||||
|
||||
async def ai_change_tone(text: str, tone: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_change_tone(text: str, tone: str, provider: ProviderName | None = None) -> str:
|
||||
"""Change the tone of the text."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Rewrite this text in a {tone} tone. Preserve the original meaning.\n\n{text}",
|
||||
@@ -196,7 +197,7 @@ async def ai_change_tone(text: str, tone: str, provider: Optional[ProviderName]
|
||||
)
|
||||
|
||||
|
||||
async def ai_translate(text: str, target_lang: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_translate(text: str, target_lang: str, provider: ProviderName | None = None) -> str:
|
||||
"""Translate text to target language."""
|
||||
# Gemini is better at translation
|
||||
if provider is None and PROVIDERS["gemini"]["api_key"]:
|
||||
@@ -212,7 +213,7 @@ async def ai_translate(text: str, target_lang: str, provider: Optional[ProviderN
|
||||
)
|
||||
|
||||
|
||||
async def ai_explain(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_explain(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Explain the selected text."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Explain the following text clearly and concisely:\n\n{text}",
|
||||
@@ -220,7 +221,7 @@ async def ai_explain(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def ai_summarize(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_summarize(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Summarize the selected text."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Summarize the following text concisely:\n\n{text}",
|
||||
@@ -228,7 +229,7 @@ async def ai_summarize(text: str, provider: Optional[ProviderName] = None) -> st
|
||||
)
|
||||
|
||||
|
||||
async def ai_continue_writing(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_continue_writing(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Continue writing from the selected text."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Continue writing from where this text leaves off. Match the style and tone:\n\n{text}",
|
||||
@@ -236,7 +237,7 @@ async def ai_continue_writing(text: str, provider: Optional[ProviderName] = None
|
||||
)
|
||||
|
||||
|
||||
async def ai_custom_rewrite(text: str, instruction: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_custom_rewrite(text: str, instruction: str, provider: ProviderName | None = None) -> str:
|
||||
"""Rewrite text based on a custom instruction."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Rewrite the following text according to this instruction: {instruction}\n\n{text}",
|
||||
@@ -244,7 +245,7 @@ async def ai_custom_rewrite(text: str, instruction: str, provider: Optional[Prov
|
||||
)
|
||||
|
||||
|
||||
async def ai_convert_to_list(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_convert_to_list(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Convert paragraph text to a markdown list."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Convert this text into a well-organized markdown bullet list. Extract key points.\n\n{text}",
|
||||
@@ -252,7 +253,7 @@ async def ai_convert_to_list(text: str, provider: Optional[ProviderName] = None)
|
||||
)
|
||||
|
||||
|
||||
async def ai_convert_to_table(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_convert_to_table(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Convert text to a markdown table."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Convert this information into a markdown table. Choose appropriate columns.\n\n{text}",
|
||||
@@ -260,7 +261,7 @@ async def ai_convert_to_table(text: str, provider: Optional[ProviderName] = None
|
||||
)
|
||||
|
||||
|
||||
async def ai_generate_frontmatter(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_generate_frontmatter(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Generate YAML frontmatter for a markdown document."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Generate YAML frontmatter for this markdown document. Include: titre, tags (as list), catégorie, statut, date. Return ONLY the YAML between --- markers.\n\n{text}",
|
||||
@@ -268,7 +269,7 @@ async def ai_generate_frontmatter(text: str, provider: Optional[ProviderName] =
|
||||
)
|
||||
|
||||
|
||||
async def ai_inline_complete(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_inline_complete(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Inline completion — suggest continuation."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Complete this text naturally. Return only the completion (just the new text, no repetition):\n\n{text}",
|
||||
@@ -276,7 +277,7 @@ async def ai_inline_complete(text: str, provider: Optional[ProviderName] = None)
|
||||
)
|
||||
|
||||
|
||||
async def ai_convert_to_canvas(text: str, provider: Optional[ProviderName] = None) -> str:
|
||||
async def ai_convert_to_canvas(text: str, provider: ProviderName | None = None) -> str:
|
||||
"""Convert text to a Mermaid diagram or canvas representation."""
|
||||
return await _call_deepseek_openrouter(
|
||||
f"Convert this content into a Mermaid.js diagram if applicable, or a structured outline. Choose the best format.\n\n{text}",
|
||||
|
||||
Reference in New Issue
Block a user