fix: forgot to lint/format

This commit is contained in:
2026-01-01 03:57:37 +01:00
parent c50091f6bf
commit ab1df3dd0f
13 changed files with 58 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
"""Agent module for media library management.""" """Agent module for media library management."""
from .agent import Agent
from alfred.settings import settings from alfred.settings import settings
from .agent import Agent
__all__ = ["Agent", "settings"] __all__ = ["Agent", "settings"]

View File

@@ -6,8 +6,8 @@ from collections.abc import AsyncGenerator
from typing import Any from typing import Any
from alfred.infrastructure.persistence import get_memory from alfred.infrastructure.persistence import get_memory
from alfred.settings import settings from alfred.settings import settings
from .prompts import PromptBuilder from .prompts import PromptBuilder
from .registry import Tool, make_tools from .registry import Tool, make_tools

View File

@@ -6,7 +6,8 @@ from typing import Any
import requests import requests
from requests.exceptions import HTTPError, RequestException, Timeout from requests.exceptions import HTTPError, RequestException, Timeout
from alfred.settings import settings, Settings from alfred.settings import Settings, settings
from .exceptions import LLMAPIError, LLMConfigurationError from .exceptions import LLMAPIError, LLMConfigurationError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -1,13 +1,13 @@
"""Ollama LLM client with robust error handling.""" """Ollama LLM client with robust error handling."""
import logging import logging
import os
from typing import Any from typing import Any
import requests import requests
from requests.exceptions import HTTPError, RequestException, Timeout from requests.exceptions import HTTPError, RequestException, Timeout
from alfred.settings import Settings, settings from alfred.settings import Settings
from .exceptions import LLMAPIError, LLMConfigurationError from .exceptions import LLMAPIError, LLMConfigurationError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -2,23 +2,21 @@
import json import json
import logging import logging
import os
import time import time
import uuid import uuid
from pathlib import Path
from typing import Any from typing import Any
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse from fastapi.responses import JSONResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from pathlib import Path
from pydantic import BaseModel, Field, validator from pydantic import BaseModel, Field, validator
from alfred.agent.agent import Agent from alfred.agent.agent import Agent
from alfred.agent.llm.deepseek import DeepSeekClient from alfred.agent.llm.deepseek import DeepSeekClient
from alfred.agent.llm.exceptions import LLMAPIError, LLMConfigurationError from alfred.agent.llm.exceptions import LLMAPIError, LLMConfigurationError
from alfred.agent.llm.ollama import OllamaClient from alfred.agent.llm.ollama import OllamaClient
from alfred.settings import settings
from alfred.infrastructure.persistence import get_memory, init_memory from alfred.infrastructure.persistence import get_memory, init_memory
from alfred.settings import settings
logging.basicConfig( logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
@@ -55,7 +53,9 @@ except LLMConfigurationError as e:
raise raise
# Initialize agent # Initialize agent
agent = Agent(settings=settings, llm=llm, max_tool_iterations=settings.max_tool_iterations) agent = Agent(
settings=settings, llm=llm, max_tool_iterations=settings.max_tool_iterations
)
logger.info("Agent Media API initialized") logger.info("Agent Media API initialized")

View File

@@ -1,9 +1,10 @@
import secrets import secrets
import tomllib
from pathlib import Path from pathlib import Path
from typing import NamedTuple
import tomllib
from pydantic import Field, computed_field, field_validator from pydantic import Field, computed_field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import NamedTuple, Optional
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
ENV_FILE_PATH = BASE_DIR / ".env" ENV_FILE_PATH = BASE_DIR / ".env"
@@ -12,13 +13,16 @@ toml_path = BASE_DIR / "pyproject.toml"
class ConfigurationError(Exception): class ConfigurationError(Exception):
"""Raised when configuration is invalid.""" """Raised when configuration is invalid."""
pass pass
class ProjectVersions(NamedTuple): class ProjectVersions(NamedTuple):
""" """
Immutable structure for project versions. Immutable structure for project versions.
Forces explicit naming and prevents accidental swaps. Forces explicit naming and prevents accidental swaps.
""" """
librechat: str librechat: str
rag: str rag: str
@@ -36,21 +40,23 @@ def get_versions_from_toml() -> ProjectVersions:
data = tomllib.load(f) data = tomllib.load(f)
try: try:
return ProjectVersions( return ProjectVersions(
librechat = data["tool"]["alfred"]["settings"]["librechat_version"], librechat=data["tool"]["alfred"]["settings"]["librechat_version"],
rag = data["tool"]["alfred"]["settings"]["rag_version"] rag=data["tool"]["alfred"]["settings"]["rag_version"],
) )
except KeyError as e: except KeyError as e:
raise KeyError(f"Error: Missing key {e} in pyproject.toml") raise KeyError(f"Error: Missing key {e} in pyproject.toml") from e
# Load versions once # Load versions once
VERSIONS: ProjectVersions = get_versions_from_toml() VERSIONS: ProjectVersions = get_versions_from_toml()
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict( model_config = SettingsConfigDict(
env_file=ENV_FILE_PATH, env_file=ENV_FILE_PATH,
env_file_encoding="utf-8", env_file_encoding="utf-8",
extra="ignore", extra="ignore",
case_sensitive=False case_sensitive=False,
) )
# --- GENERAL SETTINGS --- # --- GENERAL SETTINGS ---
host: str = "0.0.0.0" host: str = "0.0.0.0"
@@ -66,16 +72,16 @@ class Settings(BaseSettings):
max_tool_iterations: int = 10 max_tool_iterations: int = 10
request_timeout: int = 30 request_timeout: int = 30
#TODO: Finish # TODO: Finish
deepseek_base_url: str = "https://api.deepseek.com" deepseek_base_url: str = "https://api.deepseek.com"
deepseek_model: str = "deepseek-chat" deepseek_model: str = "deepseek-chat"
# --- API KEYS --- # --- API KEYS ---
anthropic_api_key: Optional[str] = Field(None, description="Claude API key") anthropic_api_key: str | None = Field(None, description="Claude API key")
deepseek_api_key: Optional[str] = Field(None, description="Deepseek API key") deepseek_api_key: str | None = Field(None, description="Deepseek API key")
google_api_key: Optional[str] = Field(None, description="Gemini API key") google_api_key: str | None = Field(None, description="Gemini API key")
kimi_api_key: Optional[str] = Field(None, description="Kimi API key") kimi_api_key: str | None = Field(None, description="Kimi API key")
openai_api_key: Optional[str] = Field(None, description="ChatGPT API key") openai_api_key: str | None = Field(None, description="ChatGPT API key")
# --- SECURITY KEYS --- # --- SECURITY KEYS ---
# Generated automatically if not in .env to ensure "Secure by Default" # Generated automatically if not in .env to ensure "Secure by Default"
@@ -93,8 +99,9 @@ class Settings(BaseSettings):
mongo_host: str = "mongodb" mongo_host: str = "mongodb"
mongo_user: str = "alfred" mongo_user: str = "alfred"
mongo_password: str = Field(default_factory=lambda: secrets.token_urlsafe(24), mongo_password: str = Field(
repr=False, exclude=True) default_factory=lambda: secrets.token_urlsafe(24), repr=False, exclude=True
)
mongo_port: int = 27017 mongo_port: int = 27017
mongo_db_name: str = "alfred" mongo_db_name: str = "alfred"
@@ -109,8 +116,9 @@ class Settings(BaseSettings):
postgres_host: str = "vectordb" postgres_host: str = "vectordb"
postgres_user: str = "alfred" postgres_user: str = "alfred"
postgres_password: str = Field(default_factory=lambda: secrets.token_urlsafe(24), postgres_password: str = Field(
repr=False, exclude=True) default_factory=lambda: secrets.token_urlsafe(24), repr=False, exclude=True
)
postgres_port: int = 5432 postgres_port: int = 5432
postgres_db_name: str = "alfred" postgres_db_name: str = "alfred"
@@ -122,7 +130,7 @@ class Settings(BaseSettings):
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db_name}" f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db_name}"
) )
tmdb_api_key: Optional[str] = Field(None, description="The Movie Database API key") tmdb_api_key: str | None = Field(None, description="The Movie Database API key")
tmdb_base_url: str = "https://api.themoviedb.org/3" tmdb_base_url: str = "https://api.themoviedb.org/3"
# --- LLM PICKER & CONFIG --- # --- LLM PICKER & CONFIG ---
@@ -134,7 +142,7 @@ class Settings(BaseSettings):
llm_temperature: float = 0.2 llm_temperature: float = 0.2
# --- RAG ENGINE --- # --- RAG ENGINE ---
rag_enabled: bool = True # TODO: Handle False rag_enabled: bool = True # TODO: Handle False
rag_api_url: str = "http://rag_api:8000" rag_api_url: str = "http://rag_api:8000"
embeddings_provider: str = "ollama" embeddings_provider: str = "ollama"
# Models: ... # Models: ...
@@ -144,10 +152,10 @@ class Settings(BaseSettings):
meili_enabled: bool = Field(True, description="Enable meili") meili_enabled: bool = Field(True, description="Enable meili")
meili_no_analytics: bool = True meili_no_analytics: bool = True
meili_host: str = "http://meilisearch:7700" meili_host: str = "http://meilisearch:7700"
meili_master_key:str = Field( meili_master_key: str = Field(
default_factory=lambda: secrets.token_urlsafe(32), default_factory=lambda: secrets.token_urlsafe(32),
description="Master key for Meilisearch", description="Master key for Meilisearch",
repr=False repr=False,
) )
# --- VALIDATORS --- # --- VALIDATORS ---
@@ -155,21 +163,27 @@ class Settings(BaseSettings):
@classmethod @classmethod
def validate_temperature(cls, v: float) -> float: def validate_temperature(cls, v: float) -> float:
if not 0.0 <= v <= 2.0: if not 0.0 <= v <= 2.0:
raise ConfigurationError(f"Temperature must be between 0.0 and 2.0, got {v}") raise ConfigurationError(
f"Temperature must be between 0.0 and 2.0, got {v}"
)
return v return v
@field_validator("max_tool_iterations") @field_validator("max_tool_iterations")
@classmethod @classmethod
def validate_max_iterations(cls, v: int) -> int: def validate_max_iterations(cls, v: int) -> int:
if not 1 <= v <= 20: if not 1 <= v <= 20:
raise ConfigurationError(f"max_tool_iterations must be between 1 and 50, got {v}") raise ConfigurationError(
f"max_tool_iterations must be between 1 and 50, got {v}"
)
return v return v
@field_validator("request_timeout") @field_validator("request_timeout")
@classmethod @classmethod
def validate_timeout(cls, v: int) -> int: def validate_timeout(cls, v: int) -> int:
if not 1 <= v <= 300: if not 1 <= v <= 300:
raise ConfigurationError(f"request_timeout must be between 1 and 300 seconds, got {v}") raise ConfigurationError(
f"request_timeout must be between 1 and 300 seconds, got {v}"
)
return v return v
@field_validator("deepseek_base_url", "tmdb_base_url") @field_validator("deepseek_base_url", "tmdb_base_url")
@@ -188,4 +202,5 @@ class Settings(BaseSettings):
def dump_safe(self): def dump_safe(self):
return self.model_dump(exclude_none=False) return self.model_dump(exclude_none=False)
settings = Settings() settings = Settings()

View File

@@ -11,9 +11,8 @@ from unittest.mock import MagicMock, Mock
import pytest import pytest
from alfred.settings import Settings, settings
from alfred.infrastructure.persistence import Memory, set_memory from alfred.infrastructure.persistence import Memory, set_memory
from alfred.settings import settings
@pytest.fixture @pytest.fixture

View File

@@ -2,7 +2,6 @@
from unittest.mock import Mock from unittest.mock import Mock
from conftest import mock_llm
from alfred.agent.agent import Agent from alfred.agent.agent import Agent
from alfred.infrastructure.persistence import get_memory from alfred.infrastructure.persistence import get_memory
@@ -142,7 +141,9 @@ class TestStep:
assert history[0]["content"] == "Hi there" assert history[0]["content"] == "Hi there"
assert history[1]["role"] == "assistant" assert history[1]["role"] == "assistant"
def test_step_with_tool_call(self, memory, mock_settings, mock_llm_with_tool_call, real_folder): def test_step_with_tool_call(
self, memory, mock_settings, mock_llm_with_tool_call, real_folder
):
"""Should execute tool and continue.""" """Should execute tool and continue."""
memory.ltm.set_config("download_folder", str(real_folder["downloads"])) memory.ltm.set_config("download_folder", str(real_folder["downloads"]))

View File

@@ -7,7 +7,6 @@ import pytest
from alfred.agent.agent import Agent from alfred.agent.agent import Agent
from alfred.infrastructure.persistence import get_memory from alfred.infrastructure.persistence import get_memory
from alfred.settings import settings from alfred.settings import settings
from conftest import mock_llm
class TestExecuteToolCallEdgeCases: class TestExecuteToolCallEdgeCases:

View File

@@ -1,8 +1,8 @@
"""Edge case tests for FastAPI endpoints.""" """Edge case tests for FastAPI endpoints."""
import pytest
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient

View File

@@ -2,7 +2,7 @@
import pytest import pytest
from alfred.settings import Settings, ConfigurationError from alfred.settings import ConfigurationError, Settings
class TestConfigValidation: class TestConfigValidation:

View File

@@ -5,13 +5,13 @@ from unittest.mock import patch
import pytest import pytest
from alfred.settings import Settings, ConfigurationError
from alfred.agent.parameters import ( from alfred.agent.parameters import (
REQUIRED_PARAMETERS, REQUIRED_PARAMETERS,
ParameterSchema, ParameterSchema,
format_parameters_for_prompt, format_parameters_for_prompt,
get_missing_required_parameters, get_missing_required_parameters,
) )
from alfred.settings import ConfigurationError, Settings
class TestSettingsEdgeCases: class TestSettingsEdgeCases:

View File

@@ -4,6 +4,7 @@ from alfred.agent.prompts import PromptBuilder
from alfred.agent.registry import make_tools from alfred.agent.registry import make_tools
from alfred.settings import settings from alfred.settings import settings
class TestPromptBuilderEdgeCases: class TestPromptBuilderEdgeCases:
"""Edge case tests for PromptBuilder.""" """Edge case tests for PromptBuilder."""